@product7/feedback-sdk 1.4.8 → 1.4.9

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,133 @@
3498
3498
  this._notify('messagesUpdate', { conversationId, messages });
3499
3499
  }
3500
3500
 
3501
- addMessage(conversationId, message) {
3501
+ _getMessageAttachmentsSignature(message) {
3502
+ if (!Array.isArray(message?.attachments) || message.attachments.length === 0) {
3503
+ return '';
3504
+ }
3505
+
3506
+ return message.attachments
3507
+ .map((att) => `${att?.type || ''}:${att?.name || ''}`)
3508
+ .join('|');
3509
+ }
3510
+
3511
+ _findOptimisticMatchIndex(conversationId, incomingMessage, matchWindowMs) {
3512
+ const messages = this.messages[conversationId] || [];
3513
+ const incomingTimestamp = Date.parse(incomingMessage.timestamp);
3514
+ const incomingSignature =
3515
+ this._getMessageAttachmentsSignature(incomingMessage);
3516
+
3517
+ for (let i = messages.length - 1; i >= 0; i--) {
3518
+ const candidate = messages[i];
3519
+ if (!candidate?.isOptimistic || !candidate?.isOwn) {
3520
+ continue;
3521
+ }
3522
+ if ((candidate.content || '') !== (incomingMessage.content || '')) {
3523
+ continue;
3524
+ }
3525
+
3526
+ const candidateSignature =
3527
+ this._getMessageAttachmentsSignature(candidate);
3528
+ if (candidateSignature !== incomingSignature) {
3529
+ continue;
3530
+ }
3531
+
3532
+ const candidateTimestamp = Date.parse(candidate.timestamp);
3533
+ if (
3534
+ Number.isNaN(incomingTimestamp) ||
3535
+ Number.isNaN(candidateTimestamp) ||
3536
+ Math.abs(incomingTimestamp - candidateTimestamp) <= matchWindowMs
3537
+ ) {
3538
+ return i;
3539
+ }
3540
+ }
3541
+
3542
+ return -1;
3543
+ }
3544
+
3545
+ _updateConversationFromMessage(conversationId, message, countUnread) {
3546
+ const conv = this.conversations.find((c) => c.id === conversationId);
3547
+ if (!conv) {
3548
+ return;
3549
+ }
3550
+
3551
+ conv.lastMessage = message.content;
3552
+ conv.lastMessageTime = message.timestamp;
3553
+
3554
+ if (countUnread && !message.isOwn) {
3555
+ conv.unread = (conv.unread || 0) + 1;
3556
+ this._updateUnreadCount();
3557
+ }
3558
+ }
3559
+
3560
+ upsertMessage(conversationId, message, options = {}) {
3502
3561
  if (!this.messages[conversationId]) {
3503
3562
  this.messages[conversationId] = [];
3504
3563
  }
3505
- this.messages[conversationId].push(message);
3506
3564
 
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();
3565
+ const reconcileOwnOptimistic = options.reconcileOwnOptimistic === true;
3566
+ const optimisticMatchWindowMs = options.optimisticMatchWindowMs || 30000;
3567
+ const messages = this.messages[conversationId];
3568
+ const existingIndex =
3569
+ message?.id != null
3570
+ ? messages.findIndex((msg) => msg?.id === message.id)
3571
+ : -1;
3572
+
3573
+ if (existingIndex !== -1) {
3574
+ messages[existingIndex] = {
3575
+ ...messages[existingIndex],
3576
+ ...message,
3577
+ isOptimistic: false,
3578
+ };
3579
+ this._updateConversationFromMessage(
3580
+ conversationId,
3581
+ messages[existingIndex],
3582
+ false
3583
+ );
3584
+ this._notify('messagesUpdate', {
3585
+ conversationId,
3586
+ messages: [...messages],
3587
+ });
3588
+ return messages[existingIndex];
3589
+ }
3590
+
3591
+ if (reconcileOwnOptimistic && message?.isOwn) {
3592
+ const optimisticIndex = this._findOptimisticMatchIndex(
3593
+ conversationId,
3594
+ message,
3595
+ optimisticMatchWindowMs
3596
+ );
3597
+ if (optimisticIndex !== -1) {
3598
+ messages[optimisticIndex] = {
3599
+ ...messages[optimisticIndex],
3600
+ ...message,
3601
+ isOptimistic: false,
3602
+ };
3603
+ this._updateConversationFromMessage(
3604
+ conversationId,
3605
+ messages[optimisticIndex],
3606
+ false
3607
+ );
3608
+ this._notify('messagesUpdate', {
3609
+ conversationId,
3610
+ messages: [...messages],
3611
+ });
3612
+ return messages[optimisticIndex];
3514
3613
  }
3515
3614
  }
3516
3615
 
3517
- this._notify('messageAdded', { conversationId, message });
3616
+ const storedMessage = {
3617
+ ...message,
3618
+ isOptimistic: Boolean(message?.isOptimistic),
3619
+ };
3620
+ messages.push(storedMessage);
3621
+ this._updateConversationFromMessage(conversationId, storedMessage, true);
3622
+ this._notify('messageAdded', { conversationId, message: storedMessage });
3623
+ return storedMessage;
3624
+ }
3625
+
3626
+ addMessage(conversationId, message) {
3627
+ return this.upsertMessage(conversationId, message);
3518
3628
  }
3519
3629
 
3520
3630
  updateConversation(conversationId, updates) {
@@ -4660,6 +4770,7 @@
4660
4770
  id: 'msg_' + Date.now(),
4661
4771
  content: content,
4662
4772
  isOwn: true,
4773
+ isOptimistic: true,
4663
4774
  timestamp: new Date().toISOString(),
4664
4775
  attachments: attachmentsToSend.map((a) => ({
4665
4776
  url: a.preview,
@@ -6062,7 +6173,10 @@
6062
6173
  },
6063
6174
  };
6064
6175
 
6065
- this.messengerState.addMessage(conversation_id, localMessage);
6176
+ this.messengerState.upsertMessage(conversation_id, localMessage, {
6177
+ reconcileOwnOptimistic: true,
6178
+ optimisticMatchWindowMs: 30000,
6179
+ });
6066
6180
 
6067
6181
  if (
6068
6182
  !this.messengerState.isOpen ||