@product7/feedback-sdk 1.4.7 → 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.
- package/dist/feedback-sdk.js +159 -36
- 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/styles/feedback.js +11 -25
- package/src/widgets/ButtonWidget.js +23 -0
- package/src/widgets/MessengerWidget.js +4 -1
- package/src/widgets/messenger/MessengerState.js +120 -10
- package/src/widgets/messenger/views/ChatView.js +1 -0
package/package.json
CHANGED
package/src/styles/feedback.js
CHANGED
|
@@ -221,44 +221,30 @@ export const feedbackStyles = `
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
.feedback-panel {
|
|
224
|
-
width:
|
|
224
|
+
width: min(420px, calc(100vw - (var(--spacing-4) * 2)));
|
|
225
|
+
max-height: min(500px, calc(100vh - 88px));
|
|
225
226
|
top: auto;
|
|
226
|
-
bottom:
|
|
227
|
-
right:
|
|
228
|
-
left:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
transform: translateY(100%);
|
|
232
|
-
border-radius: var(--radius-3xl) var(--radius-3xl) 0 0;
|
|
227
|
+
bottom: 72px;
|
|
228
|
+
right: var(--spacing-4);
|
|
229
|
+
left: auto;
|
|
230
|
+
transform: translateX(calc(100% + 24px));
|
|
231
|
+
border-radius: var(--radius-2xl);
|
|
233
232
|
}
|
|
234
233
|
|
|
235
234
|
.feedback-panel.open {
|
|
236
|
-
transform:
|
|
235
|
+
transform: translateX(0);
|
|
237
236
|
}
|
|
238
237
|
|
|
239
238
|
.feedback-panel-header {
|
|
240
|
-
padding: var(--spacing-
|
|
241
|
-
position: relative;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.feedback-panel-header::before {
|
|
245
|
-
content: '';
|
|
246
|
-
position: absolute;
|
|
247
|
-
top: var(--spacing-2);
|
|
248
|
-
left: 50%;
|
|
249
|
-
transform: translateX(-50%);
|
|
250
|
-
width: 40px;
|
|
251
|
-
height: 4px;
|
|
252
|
-
background: var(--color-neutral-300);
|
|
253
|
-
border-radius: 2px;
|
|
239
|
+
padding: var(--spacing-4) var(--spacing-6);
|
|
254
240
|
}
|
|
255
241
|
|
|
256
242
|
.feedback-panel-body {
|
|
257
|
-
padding: var(--spacing-
|
|
243
|
+
padding: var(--spacing-6);
|
|
258
244
|
}
|
|
259
245
|
|
|
260
246
|
.feedback-form-group textarea {
|
|
261
|
-
min-height:
|
|
247
|
+
min-height: 120px;
|
|
262
248
|
}
|
|
263
249
|
}
|
|
264
250
|
`;
|
|
@@ -4,6 +4,7 @@ export class ButtonWidget extends BaseWidget {
|
|
|
4
4
|
constructor(options) {
|
|
5
5
|
super({ ...options, type: 'button' });
|
|
6
6
|
this.isMinimized = false;
|
|
7
|
+
this._hiddenForOpenPanel = false;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
_render() {
|
|
@@ -90,6 +91,28 @@ export class ButtonWidget extends BaseWidget {
|
|
|
90
91
|
this.element.classList.remove('minimized');
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
openPanel() {
|
|
95
|
+
if (!this.state.isOpen) {
|
|
96
|
+
this._hiddenForOpenPanel = true;
|
|
97
|
+
this.hide();
|
|
98
|
+
}
|
|
99
|
+
super.openPanel();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
closePanel() {
|
|
103
|
+
const shouldRestoreButton = this._hiddenForOpenPanel;
|
|
104
|
+
super.closePanel();
|
|
105
|
+
|
|
106
|
+
if (shouldRestoreButton) {
|
|
107
|
+
setTimeout(() => {
|
|
108
|
+
if (!this.destroyed) {
|
|
109
|
+
this.show();
|
|
110
|
+
}
|
|
111
|
+
this._hiddenForOpenPanel = false;
|
|
112
|
+
}, 320);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
mount(container) {
|
|
94
117
|
super.mount(container);
|
|
95
118
|
}
|
|
@@ -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,133 @@ export class MessengerState {
|
|
|
98
98
|
this._notify('messagesUpdate', { conversationId, messages });
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
_getMessageAttachmentsSignature(message) {
|
|
102
|
+
if (!Array.isArray(message?.attachments) || message.attachments.length === 0) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return message.attachments
|
|
107
|
+
.map((att) => `${att?.type || ''}:${att?.name || ''}`)
|
|
108
|
+
.join('|');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
_findOptimisticMatchIndex(conversationId, incomingMessage, matchWindowMs) {
|
|
112
|
+
const messages = this.messages[conversationId] || [];
|
|
113
|
+
const incomingTimestamp = Date.parse(incomingMessage.timestamp);
|
|
114
|
+
const incomingSignature =
|
|
115
|
+
this._getMessageAttachmentsSignature(incomingMessage);
|
|
116
|
+
|
|
117
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
118
|
+
const candidate = messages[i];
|
|
119
|
+
if (!candidate?.isOptimistic || !candidate?.isOwn) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if ((candidate.content || '') !== (incomingMessage.content || '')) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const candidateSignature =
|
|
127
|
+
this._getMessageAttachmentsSignature(candidate);
|
|
128
|
+
if (candidateSignature !== incomingSignature) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const candidateTimestamp = Date.parse(candidate.timestamp);
|
|
133
|
+
if (
|
|
134
|
+
Number.isNaN(incomingTimestamp) ||
|
|
135
|
+
Number.isNaN(candidateTimestamp) ||
|
|
136
|
+
Math.abs(incomingTimestamp - candidateTimestamp) <= matchWindowMs
|
|
137
|
+
) {
|
|
138
|
+
return i;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return -1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
_updateConversationFromMessage(conversationId, message, countUnread) {
|
|
146
|
+
const conv = this.conversations.find((c) => c.id === conversationId);
|
|
147
|
+
if (!conv) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
conv.lastMessage = message.content;
|
|
152
|
+
conv.lastMessageTime = message.timestamp;
|
|
153
|
+
|
|
154
|
+
if (countUnread && !message.isOwn) {
|
|
155
|
+
conv.unread = (conv.unread || 0) + 1;
|
|
156
|
+
this._updateUnreadCount();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
upsertMessage(conversationId, message, options = {}) {
|
|
102
161
|
if (!this.messages[conversationId]) {
|
|
103
162
|
this.messages[conversationId] = [];
|
|
104
163
|
}
|
|
105
|
-
this.messages[conversationId].push(message);
|
|
106
164
|
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
165
|
+
const reconcileOwnOptimistic = options.reconcileOwnOptimistic === true;
|
|
166
|
+
const optimisticMatchWindowMs = options.optimisticMatchWindowMs || 30000;
|
|
167
|
+
const messages = this.messages[conversationId];
|
|
168
|
+
const existingIndex =
|
|
169
|
+
message?.id != null
|
|
170
|
+
? messages.findIndex((msg) => msg?.id === message.id)
|
|
171
|
+
: -1;
|
|
172
|
+
|
|
173
|
+
if (existingIndex !== -1) {
|
|
174
|
+
messages[existingIndex] = {
|
|
175
|
+
...messages[existingIndex],
|
|
176
|
+
...message,
|
|
177
|
+
isOptimistic: false,
|
|
178
|
+
};
|
|
179
|
+
this._updateConversationFromMessage(
|
|
180
|
+
conversationId,
|
|
181
|
+
messages[existingIndex],
|
|
182
|
+
false
|
|
183
|
+
);
|
|
184
|
+
this._notify('messagesUpdate', {
|
|
185
|
+
conversationId,
|
|
186
|
+
messages: [...messages],
|
|
187
|
+
});
|
|
188
|
+
return messages[existingIndex];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (reconcileOwnOptimistic && message?.isOwn) {
|
|
192
|
+
const optimisticIndex = this._findOptimisticMatchIndex(
|
|
193
|
+
conversationId,
|
|
194
|
+
message,
|
|
195
|
+
optimisticMatchWindowMs
|
|
196
|
+
);
|
|
197
|
+
if (optimisticIndex !== -1) {
|
|
198
|
+
messages[optimisticIndex] = {
|
|
199
|
+
...messages[optimisticIndex],
|
|
200
|
+
...message,
|
|
201
|
+
isOptimistic: false,
|
|
202
|
+
};
|
|
203
|
+
this._updateConversationFromMessage(
|
|
204
|
+
conversationId,
|
|
205
|
+
messages[optimisticIndex],
|
|
206
|
+
false
|
|
207
|
+
);
|
|
208
|
+
this._notify('messagesUpdate', {
|
|
209
|
+
conversationId,
|
|
210
|
+
messages: [...messages],
|
|
211
|
+
});
|
|
212
|
+
return messages[optimisticIndex];
|
|
114
213
|
}
|
|
115
214
|
}
|
|
116
215
|
|
|
117
|
-
|
|
216
|
+
const storedMessage = {
|
|
217
|
+
...message,
|
|
218
|
+
isOptimistic: Boolean(message?.isOptimistic),
|
|
219
|
+
};
|
|
220
|
+
messages.push(storedMessage);
|
|
221
|
+
this._updateConversationFromMessage(conversationId, storedMessage, true);
|
|
222
|
+
this._notify('messageAdded', { conversationId, message: storedMessage });
|
|
223
|
+
return storedMessage;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
addMessage(conversationId, message) {
|
|
227
|
+
return this.upsertMessage(conversationId, message);
|
|
118
228
|
}
|
|
119
229
|
|
|
120
230
|
updateConversation(conversationId, updates) {
|