@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.
- package/dist/feedback-sdk.js +129 -12
- package/dist/feedback-sdk.js.map +1 -1
- package/dist/feedback-sdk.min.js +1 -1
- package/dist/feedback-sdk.min.js.map +1 -1
- package/package.json +1 -1
- package/src/widgets/MessengerWidget.js +4 -1
- package/src/widgets/messenger/MessengerState.js +123 -10
- package/src/widgets/messenger/views/ChatView.js +2 -1
package/package.json
CHANGED
|
@@ -348,7 +348,10 @@ export class MessengerWidget extends BaseWidget {
|
|
|
348
348
|
},
|
|
349
349
|
};
|
|
350
350
|
|
|
351
|
-
this.messengerState.
|
|
351
|
+
this.messengerState.upsertMessage(conversation_id, localMessage, {
|
|
352
|
+
reconcileOwnOptimistic: true,
|
|
353
|
+
optimisticMatchWindowMs: 30000,
|
|
354
|
+
});
|
|
352
355
|
|
|
353
356
|
if (
|
|
354
357
|
!this.messengerState.isOpen ||
|
|
@@ -98,23 +98,136 @@ export class MessengerState {
|
|
|
98
98
|
this._notify('messagesUpdate', { conversationId, messages });
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
_getMessageAttachmentsSignature(message) {
|
|
102
|
+
if (
|
|
103
|
+
!Array.isArray(message?.attachments) ||
|
|
104
|
+
message.attachments.length === 0
|
|
105
|
+
) {
|
|
106
|
+
return '';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return message.attachments
|
|
110
|
+
.map((att) => `${att?.type || ''}:${att?.name || ''}`)
|
|
111
|
+
.join('|');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_findOptimisticMatchIndex(conversationId, incomingMessage, matchWindowMs) {
|
|
115
|
+
const messages = this.messages[conversationId] || [];
|
|
116
|
+
const incomingTimestamp = Date.parse(incomingMessage.timestamp);
|
|
117
|
+
const incomingSignature =
|
|
118
|
+
this._getMessageAttachmentsSignature(incomingMessage);
|
|
119
|
+
|
|
120
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
121
|
+
const candidate = messages[i];
|
|
122
|
+
if (!candidate?.isOptimistic || !candidate?.isOwn) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if ((candidate.content || '') !== (incomingMessage.content || '')) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const candidateSignature =
|
|
130
|
+
this._getMessageAttachmentsSignature(candidate);
|
|
131
|
+
if (candidateSignature !== incomingSignature) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const candidateTimestamp = Date.parse(candidate.timestamp);
|
|
136
|
+
if (
|
|
137
|
+
Number.isNaN(incomingTimestamp) ||
|
|
138
|
+
Number.isNaN(candidateTimestamp) ||
|
|
139
|
+
Math.abs(incomingTimestamp - candidateTimestamp) <= matchWindowMs
|
|
140
|
+
) {
|
|
141
|
+
return i;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
_updateConversationFromMessage(conversationId, message, countUnread) {
|
|
149
|
+
const conv = this.conversations.find((c) => c.id === conversationId);
|
|
150
|
+
if (!conv) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
conv.lastMessage = message.content;
|
|
155
|
+
conv.lastMessageTime = message.timestamp;
|
|
156
|
+
|
|
157
|
+
if (countUnread && !message.isOwn) {
|
|
158
|
+
conv.unread = (conv.unread || 0) + 1;
|
|
159
|
+
this._updateUnreadCount();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
upsertMessage(conversationId, message, options = {}) {
|
|
102
164
|
if (!this.messages[conversationId]) {
|
|
103
165
|
this.messages[conversationId] = [];
|
|
104
166
|
}
|
|
105
|
-
this.messages[conversationId].push(message);
|
|
106
167
|
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
168
|
+
const reconcileOwnOptimistic = options.reconcileOwnOptimistic === true;
|
|
169
|
+
const optimisticMatchWindowMs = options.optimisticMatchWindowMs || 30000;
|
|
170
|
+
const messages = this.messages[conversationId];
|
|
171
|
+
const existingIndex =
|
|
172
|
+
message?.id != null
|
|
173
|
+
? messages.findIndex((msg) => msg?.id === message.id)
|
|
174
|
+
: -1;
|
|
175
|
+
|
|
176
|
+
if (existingIndex !== -1) {
|
|
177
|
+
messages[existingIndex] = {
|
|
178
|
+
...messages[existingIndex],
|
|
179
|
+
...message,
|
|
180
|
+
isOptimistic: false,
|
|
181
|
+
};
|
|
182
|
+
this._updateConversationFromMessage(
|
|
183
|
+
conversationId,
|
|
184
|
+
messages[existingIndex],
|
|
185
|
+
false
|
|
186
|
+
);
|
|
187
|
+
this._notify('messagesUpdate', {
|
|
188
|
+
conversationId,
|
|
189
|
+
messages: [...messages],
|
|
190
|
+
});
|
|
191
|
+
return messages[existingIndex];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (reconcileOwnOptimistic && message?.isOwn) {
|
|
195
|
+
const optimisticIndex = this._findOptimisticMatchIndex(
|
|
196
|
+
conversationId,
|
|
197
|
+
message,
|
|
198
|
+
optimisticMatchWindowMs
|
|
199
|
+
);
|
|
200
|
+
if (optimisticIndex !== -1) {
|
|
201
|
+
messages[optimisticIndex] = {
|
|
202
|
+
...messages[optimisticIndex],
|
|
203
|
+
...message,
|
|
204
|
+
isOptimistic: false,
|
|
205
|
+
};
|
|
206
|
+
this._updateConversationFromMessage(
|
|
207
|
+
conversationId,
|
|
208
|
+
messages[optimisticIndex],
|
|
209
|
+
false
|
|
210
|
+
);
|
|
211
|
+
this._notify('messagesUpdate', {
|
|
212
|
+
conversationId,
|
|
213
|
+
messages: [...messages],
|
|
214
|
+
});
|
|
215
|
+
return messages[optimisticIndex];
|
|
114
216
|
}
|
|
115
217
|
}
|
|
116
218
|
|
|
117
|
-
|
|
219
|
+
const storedMessage = {
|
|
220
|
+
...message,
|
|
221
|
+
isOptimistic: Boolean(message?.isOptimistic),
|
|
222
|
+
};
|
|
223
|
+
messages.push(storedMessage);
|
|
224
|
+
this._updateConversationFromMessage(conversationId, storedMessage, true);
|
|
225
|
+
this._notify('messageAdded', { conversationId, message: storedMessage });
|
|
226
|
+
return storedMessage;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
addMessage(conversationId, message) {
|
|
230
|
+
return this.upsertMessage(conversationId, message);
|
|
118
231
|
}
|
|
119
232
|
|
|
120
233
|
updateConversation(conversationId, updates) {
|
|
@@ -117,7 +117,7 @@ export class ChatView {
|
|
|
117
117
|
|
|
118
118
|
<div class="messenger-chat-compose">
|
|
119
119
|
<button class="sdk-btn-icon messenger-compose-attach" aria-label="Attach file">
|
|
120
|
-
<
|
|
120
|
+
<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>
|
|
121
121
|
</button>
|
|
122
122
|
<div class="messenger-compose-input-wrapper">
|
|
123
123
|
<textarea class="messenger-compose-input" placeholder="${placeholder}" rows="1"></textarea>
|
|
@@ -485,6 +485,7 @@ export class ChatView {
|
|
|
485
485
|
id: 'msg_' + Date.now(),
|
|
486
486
|
content: content,
|
|
487
487
|
isOwn: true,
|
|
488
|
+
isOptimistic: true,
|
|
488
489
|
timestamp: new Date().toISOString(),
|
|
489
490
|
attachments: attachmentsToSend.map((a) => ({
|
|
490
491
|
url: a.preview,
|