@product7/feedback-sdk 1.4.8 → 1.5.0

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.
@@ -3498,23 +3498,136 @@
3498
3498
  this._notify('messagesUpdate', { conversationId, messages });
3499
3499
  }
3500
3500
 
3501
- addMessage(conversationId, message) {
3501
+ _getMessageAttachmentsSignature(message) {
3502
+ if (
3503
+ !Array.isArray(message?.attachments) ||
3504
+ message.attachments.length === 0
3505
+ ) {
3506
+ return '';
3507
+ }
3508
+
3509
+ return message.attachments
3510
+ .map((att) => `${att?.type || ''}:${att?.name || ''}`)
3511
+ .join('|');
3512
+ }
3513
+
3514
+ _findOptimisticMatchIndex(conversationId, incomingMessage, matchWindowMs) {
3515
+ const messages = this.messages[conversationId] || [];
3516
+ const incomingTimestamp = Date.parse(incomingMessage.timestamp);
3517
+ const incomingSignature =
3518
+ this._getMessageAttachmentsSignature(incomingMessage);
3519
+
3520
+ for (let i = messages.length - 1; i >= 0; i--) {
3521
+ const candidate = messages[i];
3522
+ if (!candidate?.isOptimistic || !candidate?.isOwn) {
3523
+ continue;
3524
+ }
3525
+ if ((candidate.content || '') !== (incomingMessage.content || '')) {
3526
+ continue;
3527
+ }
3528
+
3529
+ const candidateSignature =
3530
+ this._getMessageAttachmentsSignature(candidate);
3531
+ if (candidateSignature !== incomingSignature) {
3532
+ continue;
3533
+ }
3534
+
3535
+ const candidateTimestamp = Date.parse(candidate.timestamp);
3536
+ if (
3537
+ Number.isNaN(incomingTimestamp) ||
3538
+ Number.isNaN(candidateTimestamp) ||
3539
+ Math.abs(incomingTimestamp - candidateTimestamp) <= matchWindowMs
3540
+ ) {
3541
+ return i;
3542
+ }
3543
+ }
3544
+
3545
+ return -1;
3546
+ }
3547
+
3548
+ _updateConversationFromMessage(conversationId, message, countUnread) {
3549
+ const conv = this.conversations.find((c) => c.id === conversationId);
3550
+ if (!conv) {
3551
+ return;
3552
+ }
3553
+
3554
+ conv.lastMessage = message.content;
3555
+ conv.lastMessageTime = message.timestamp;
3556
+
3557
+ if (countUnread && !message.isOwn) {
3558
+ conv.unread = (conv.unread || 0) + 1;
3559
+ this._updateUnreadCount();
3560
+ }
3561
+ }
3562
+
3563
+ upsertMessage(conversationId, message, options = {}) {
3502
3564
  if (!this.messages[conversationId]) {
3503
3565
  this.messages[conversationId] = [];
3504
3566
  }
3505
- this.messages[conversationId].push(message);
3506
3567
 
3507
- const conv = this.conversations.find((c) => c.id === conversationId);
3508
- if (conv) {
3509
- conv.lastMessage = message.content;
3510
- conv.lastMessageTime = message.timestamp;
3511
- if (!message.isOwn) {
3512
- conv.unread = (conv.unread || 0) + 1;
3513
- this._updateUnreadCount();
3568
+ const reconcileOwnOptimistic = options.reconcileOwnOptimistic === true;
3569
+ const optimisticMatchWindowMs = options.optimisticMatchWindowMs || 30000;
3570
+ const messages = this.messages[conversationId];
3571
+ const existingIndex =
3572
+ message?.id != null
3573
+ ? messages.findIndex((msg) => msg?.id === message.id)
3574
+ : -1;
3575
+
3576
+ if (existingIndex !== -1) {
3577
+ messages[existingIndex] = {
3578
+ ...messages[existingIndex],
3579
+ ...message,
3580
+ isOptimistic: false,
3581
+ };
3582
+ this._updateConversationFromMessage(
3583
+ conversationId,
3584
+ messages[existingIndex],
3585
+ false
3586
+ );
3587
+ this._notify('messagesUpdate', {
3588
+ conversationId,
3589
+ messages: [...messages],
3590
+ });
3591
+ return messages[existingIndex];
3592
+ }
3593
+
3594
+ if (reconcileOwnOptimistic && message?.isOwn) {
3595
+ const optimisticIndex = this._findOptimisticMatchIndex(
3596
+ conversationId,
3597
+ message,
3598
+ optimisticMatchWindowMs
3599
+ );
3600
+ if (optimisticIndex !== -1) {
3601
+ messages[optimisticIndex] = {
3602
+ ...messages[optimisticIndex],
3603
+ ...message,
3604
+ isOptimistic: false,
3605
+ };
3606
+ this._updateConversationFromMessage(
3607
+ conversationId,
3608
+ messages[optimisticIndex],
3609
+ false
3610
+ );
3611
+ this._notify('messagesUpdate', {
3612
+ conversationId,
3613
+ messages: [...messages],
3614
+ });
3615
+ return messages[optimisticIndex];
3514
3616
  }
3515
3617
  }
3516
3618
 
3517
- this._notify('messageAdded', { conversationId, message });
3619
+ const storedMessage = {
3620
+ ...message,
3621
+ isOptimistic: Boolean(message?.isOptimistic),
3622
+ };
3623
+ messages.push(storedMessage);
3624
+ this._updateConversationFromMessage(conversationId, storedMessage, true);
3625
+ this._notify('messageAdded', { conversationId, message: storedMessage });
3626
+ return storedMessage;
3627
+ }
3628
+
3629
+ addMessage(conversationId, message) {
3630
+ return this.upsertMessage(conversationId, message);
3518
3631
  }
3519
3632
 
3520
3633
  updateConversation(conversationId, updates) {
@@ -4292,7 +4405,7 @@
4292
4405
 
4293
4406
  <div class="messenger-chat-compose">
4294
4407
  <button class="sdk-btn-icon messenger-compose-attach" aria-label="Attach file">
4295
- <i class="ph ph-paperclip"></i>
4408
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="16" height="16" fill="none"/><path d="M160,80,76.69,164.69a16,16,0,0,0,22.63,22.62L198.63,86.63a32,32,0,0,0-45.26-45.26L54.06,142.06a48,48,0,0,0,67.88,67.88L204,128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
4296
4409
  </button>
4297
4410
  <div class="messenger-compose-input-wrapper">
4298
4411
  <textarea class="messenger-compose-input" placeholder="${placeholder}" rows="1"></textarea>
@@ -4660,6 +4773,7 @@
4660
4773
  id: 'msg_' + Date.now(),
4661
4774
  content: content,
4662
4775
  isOwn: true,
4776
+ isOptimistic: true,
4663
4777
  timestamp: new Date().toISOString(),
4664
4778
  attachments: attachmentsToSend.map((a) => ({
4665
4779
  url: a.preview,
@@ -6062,7 +6176,10 @@
6062
6176
  },
6063
6177
  };
6064
6178
 
6065
- this.messengerState.addMessage(conversation_id, localMessage);
6179
+ this.messengerState.upsertMessage(conversation_id, localMessage, {
6180
+ reconcileOwnOptimistic: true,
6181
+ optimisticMatchWindowMs: 30000,
6182
+ });
6066
6183
 
6067
6184
  if (
6068
6185
  !this.messengerState.isOpen ||