@product7/feedback-sdk 1.3.0 → 1.3.2
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 +2158 -663
- 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 +2 -1
- package/src/core/APIService.js +191 -5
- package/src/core/FeedbackSDK.js +3 -0
- package/src/styles/messengerStyles.js +580 -9
- package/src/widgets/MessengerWidget.js +247 -137
- package/src/widgets/messenger/MessengerState.js +31 -1
- package/src/widgets/messenger/views/ChatView.js +347 -29
- package/src/widgets/messenger/views/ConversationsView.js +20 -5
- package/src/widgets/messenger/views/HomeView.js +50 -10
- package/src/widgets/messenger/views/PreChatFormView.js +224 -0
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* MessengerWidget - Full-featured Messenger/Chat widget
|
|
3
|
-
*/
|
|
4
|
-
import { WebSocketService } from '../core/WebSocketService.js';
|
|
1
|
+
|
|
5
2
|
import { BaseWidget } from './BaseWidget.js';
|
|
6
3
|
import { MessengerState } from './messenger/MessengerState.js';
|
|
7
4
|
import { MessengerLauncher } from './messenger/components/MessengerLauncher.js';
|
|
@@ -11,6 +8,8 @@ import { ChatView } from './messenger/views/ChatView.js';
|
|
|
11
8
|
import { ConversationsView } from './messenger/views/ConversationsView.js';
|
|
12
9
|
import { HelpView } from './messenger/views/HelpView.js';
|
|
13
10
|
import { HomeView } from './messenger/views/HomeView.js';
|
|
11
|
+
import { PreChatFormView } from './messenger/views/PreChatFormView.js';
|
|
12
|
+
import { WebSocketService } from '../core/WebSocketService.js';
|
|
14
13
|
|
|
15
14
|
export class MessengerWidget extends BaseWidget {
|
|
16
15
|
constructor(options) {
|
|
@@ -27,13 +26,11 @@ export class MessengerWidget extends BaseWidget {
|
|
|
27
26
|
logoUrl: options.logoUrl || 'https://product7.io/p7logo.svg',
|
|
28
27
|
featuredContent: options.featuredContent || null,
|
|
29
28
|
primaryColor: options.primaryColor || '#1c1c1e',
|
|
30
|
-
// Callbacks
|
|
31
29
|
onSendMessage: options.onSendMessage || null,
|
|
32
30
|
onArticleClick: options.onArticleClick || null,
|
|
33
31
|
onChangelogClick: options.onChangelogClick || null,
|
|
34
32
|
};
|
|
35
33
|
|
|
36
|
-
// Create state
|
|
37
34
|
this.messengerState = new MessengerState({
|
|
38
35
|
teamName: this.messengerOptions.teamName,
|
|
39
36
|
teamAvatars: this.messengerOptions.teamAvatars,
|
|
@@ -48,51 +45,46 @@ export class MessengerWidget extends BaseWidget {
|
|
|
48
45
|
this.wsService = null;
|
|
49
46
|
this._wsUnsubscribers = [];
|
|
50
47
|
|
|
51
|
-
// Bind methods
|
|
52
48
|
this._handleOpenChange = this._handleOpenChange.bind(this);
|
|
53
49
|
this._handleWebSocketMessage = this._handleWebSocketMessage.bind(this);
|
|
54
50
|
this._handleTypingStarted = this._handleTypingStarted.bind(this);
|
|
55
51
|
this._handleTypingStopped = this._handleTypingStopped.bind(this);
|
|
52
|
+
this._handleConversationClosed = this._handleConversationClosed.bind(this);
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
_render() {
|
|
59
|
-
// Create container
|
|
60
56
|
const container = document.createElement('div');
|
|
61
57
|
container.className = `messenger-widget theme-${this.messengerOptions.theme}`;
|
|
62
58
|
container.style.zIndex = '999999';
|
|
63
59
|
|
|
64
|
-
// Create launcher
|
|
65
60
|
this.launcher = new MessengerLauncher(this.messengerState, {
|
|
66
61
|
position: this.messengerOptions.position,
|
|
67
62
|
primaryColor: this.messengerOptions.primaryColor,
|
|
68
63
|
});
|
|
69
64
|
container.appendChild(this.launcher.render());
|
|
70
65
|
|
|
71
|
-
// Create panel with all callbacks
|
|
72
66
|
this.panel = new MessengerPanel(this.messengerState, {
|
|
73
67
|
position: this.messengerOptions.position,
|
|
74
68
|
theme: this.messengerOptions.theme,
|
|
75
69
|
primaryColor: this.messengerOptions.primaryColor,
|
|
76
70
|
logoUrl: this.messengerOptions.logoUrl,
|
|
77
71
|
featuredContent: this.messengerOptions.featuredContent,
|
|
78
|
-
// Chat callbacks
|
|
79
72
|
onSendMessage:
|
|
80
73
|
this.messengerOptions.onSendMessage ||
|
|
81
74
|
this._handleSendMessage.bind(this),
|
|
82
75
|
onStartConversation: this._handleStartConversation.bind(this),
|
|
83
76
|
onTyping: this.sendTypingIndicator.bind(this),
|
|
84
|
-
// Conversation list callbacks
|
|
85
77
|
onSelectConversation: this._handleSelectConversation.bind(this),
|
|
86
78
|
onStartNewConversation: this._handleNewConversationClick.bind(this),
|
|
87
|
-
|
|
79
|
+
onIdentifyContact: this._handleIdentifyContact.bind(this),
|
|
88
80
|
onArticleClick: this.messengerOptions.onArticleClick,
|
|
89
81
|
onChangelogClick: this.messengerOptions.onChangelogClick,
|
|
90
82
|
});
|
|
91
83
|
|
|
92
|
-
// Register views
|
|
93
84
|
this.panel.registerView('home', HomeView);
|
|
94
85
|
this.panel.registerView('messages', ConversationsView);
|
|
95
86
|
this.panel.registerView('chat', ChatView);
|
|
87
|
+
this.panel.registerView('prechat', PreChatFormView);
|
|
96
88
|
this.panel.registerView('help', HelpView);
|
|
97
89
|
this.panel.registerView('changelog', ChangelogView);
|
|
98
90
|
|
|
@@ -103,11 +95,16 @@ export class MessengerWidget extends BaseWidget {
|
|
|
103
95
|
}
|
|
104
96
|
|
|
105
97
|
_attachEvents() {
|
|
106
|
-
// Subscribe to state changes
|
|
107
98
|
this._stateUnsubscribe = this.messengerState.subscribe((type, data) => {
|
|
108
99
|
if (type === 'openChange') {
|
|
109
100
|
this._handleOpenChange(data.isOpen);
|
|
110
101
|
}
|
|
102
|
+
if (type === 'conversationChange') {
|
|
103
|
+
this._handleActiveConversationChange(
|
|
104
|
+
data.conversationId,
|
|
105
|
+
data.previousConversationId
|
|
106
|
+
);
|
|
107
|
+
}
|
|
111
108
|
});
|
|
112
109
|
}
|
|
113
110
|
|
|
@@ -122,20 +119,58 @@ export class MessengerWidget extends BaseWidget {
|
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
121
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
122
|
+
_handleActiveConversationChange(conversationId, previousConversationId) {
|
|
123
|
+
if (previousConversationId && this.wsService) {
|
|
124
|
+
this.wsService.send('conversation:unsubscribe', {
|
|
125
|
+
conversation_id: previousConversationId,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (conversationId && this.wsService) {
|
|
129
|
+
this.wsService.send('conversation:subscribe', {
|
|
130
|
+
conversation_id: conversationId,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async _handleStartConversation(messageContent, pendingAttachments) {
|
|
129
136
|
try {
|
|
130
|
-
|
|
137
|
+
const userContext = this.messengerState.userContext;
|
|
138
|
+
const isIdentified = userContext?.email;
|
|
139
|
+
|
|
140
|
+
if (!isIdentified) {
|
|
141
|
+
this.messengerState.pendingMessage = {
|
|
142
|
+
content: messageContent,
|
|
143
|
+
attachments: pendingAttachments,
|
|
144
|
+
};
|
|
145
|
+
this.messengerState.setView('prechat');
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const openConversation = this.messengerState.conversations.find(
|
|
150
|
+
(c) => c.status === 'open'
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
if (openConversation) {
|
|
154
|
+
this.messengerState.setActiveConversation(openConversation.id);
|
|
155
|
+
await this._handleSendMessage(
|
|
156
|
+
openConversation.id,
|
|
157
|
+
{ content: messageContent },
|
|
158
|
+
pendingAttachments
|
|
159
|
+
);
|
|
160
|
+
return openConversation;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return await this.startNewConversation(
|
|
164
|
+
messageContent,
|
|
165
|
+
'',
|
|
166
|
+
pendingAttachments
|
|
167
|
+
);
|
|
131
168
|
} catch (error) {
|
|
132
169
|
console.error('[MessengerWidget] Failed to start conversation:', error);
|
|
170
|
+
return null;
|
|
133
171
|
}
|
|
134
172
|
}
|
|
135
173
|
|
|
136
|
-
/**
|
|
137
|
-
* Handle selecting a conversation from the list
|
|
138
|
-
*/
|
|
139
174
|
async _handleSelectConversation(conversationId) {
|
|
140
175
|
try {
|
|
141
176
|
await this.fetchMessages(conversationId);
|
|
@@ -144,16 +179,95 @@ export class MessengerWidget extends BaseWidget {
|
|
|
144
179
|
}
|
|
145
180
|
}
|
|
146
181
|
|
|
147
|
-
/**
|
|
148
|
-
* Handle clicking "new conversation" button
|
|
149
|
-
*/
|
|
150
182
|
_handleNewConversationClick() {
|
|
151
|
-
|
|
152
|
-
|
|
183
|
+
const openConversation = this.messengerState.conversations.find(
|
|
184
|
+
(c) => c.status === 'open'
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
if (openConversation) {
|
|
188
|
+
this.messengerState.setActiveConversation(openConversation.id);
|
|
189
|
+
this.messengerState.setView('chat');
|
|
190
|
+
this._handleSelectConversation(openConversation.id);
|
|
191
|
+
} else {
|
|
192
|
+
this.messengerState.setActiveConversation(null);
|
|
193
|
+
this.messengerState.setView('chat');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async _handleIdentifyContact(contactData) {
|
|
198
|
+
try {
|
|
199
|
+
const response = await this.apiService.identifyContact({
|
|
200
|
+
name: contactData.name,
|
|
201
|
+
email: contactData.email,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (response.status) {
|
|
205
|
+
console.log('[MessengerWidget] Contact identified:', contactData.email);
|
|
206
|
+
|
|
207
|
+
if (!this.messengerState.userContext) {
|
|
208
|
+
this.messengerState.userContext = {};
|
|
209
|
+
}
|
|
210
|
+
this.messengerState.userContext.name = contactData.name;
|
|
211
|
+
this.messengerState.userContext.email = contactData.email;
|
|
212
|
+
|
|
213
|
+
const pendingMessage = this.messengerState.pendingMessage;
|
|
214
|
+
if (pendingMessage) {
|
|
215
|
+
this.messengerState.pendingMessage = null;
|
|
216
|
+
|
|
217
|
+
await this.startNewConversation(
|
|
218
|
+
pendingMessage.content,
|
|
219
|
+
'',
|
|
220
|
+
pendingMessage.attachments
|
|
221
|
+
);
|
|
222
|
+
} else {
|
|
223
|
+
this.messengerState.setView('chat');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return response;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error('[MessengerWidget] Failed to identify contact:', error);
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async _handleUploadFile(base64Data, filename) {
|
|
235
|
+
try {
|
|
236
|
+
const response = await this.apiService.uploadFile(base64Data, filename);
|
|
237
|
+
if (response.status && response.url) {
|
|
238
|
+
return response.url;
|
|
239
|
+
}
|
|
240
|
+
throw new Error('Upload failed');
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.error('[MessengerWidget] Failed to upload file:', error);
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
153
245
|
}
|
|
154
246
|
|
|
155
|
-
async
|
|
156
|
-
|
|
247
|
+
async _uploadPendingAttachments(pendingAttachments) {
|
|
248
|
+
if (!pendingAttachments || pendingAttachments.length === 0) return [];
|
|
249
|
+
|
|
250
|
+
const uploaded = [];
|
|
251
|
+
for (const att of pendingAttachments) {
|
|
252
|
+
try {
|
|
253
|
+
const cdnUrl = await this._handleUploadFile(att.preview, att.file.name);
|
|
254
|
+
uploaded.push({
|
|
255
|
+
url: cdnUrl,
|
|
256
|
+
type: att.type.startsWith('image') ? 'image' : 'file',
|
|
257
|
+
name: att.file.name,
|
|
258
|
+
});
|
|
259
|
+
} catch (err) {
|
|
260
|
+
console.error(
|
|
261
|
+
'[MessengerWidget] Skipping failed attachment upload:',
|
|
262
|
+
att.file.name,
|
|
263
|
+
err
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return uploaded;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async _handleSendMessage(conversationId, message, pendingAttachments) {
|
|
157
271
|
this.sdk.eventBus.emit('messenger:messageSent', {
|
|
158
272
|
widget: this,
|
|
159
273
|
conversationId,
|
|
@@ -161,18 +275,18 @@ export class MessengerWidget extends BaseWidget {
|
|
|
161
275
|
});
|
|
162
276
|
|
|
163
277
|
try {
|
|
164
|
-
|
|
278
|
+
const uploadedAttachments =
|
|
279
|
+
await this._uploadPendingAttachments(pendingAttachments);
|
|
280
|
+
|
|
165
281
|
const response = await this.apiService.sendMessage(conversationId, {
|
|
166
282
|
content: message.content,
|
|
283
|
+
attachments: uploadedAttachments,
|
|
167
284
|
});
|
|
168
285
|
|
|
169
286
|
if (response.status && response.data) {
|
|
170
|
-
// Update the message ID with server-assigned ID
|
|
171
|
-
// Message is already added to state optimistically in ChatView
|
|
172
287
|
console.log('[MessengerWidget] Message sent:', response.data.id);
|
|
173
288
|
}
|
|
174
289
|
|
|
175
|
-
// In mock mode, simulate an agent response after a delay
|
|
176
290
|
if (this.apiService?.mock) {
|
|
177
291
|
setTimeout(() => {
|
|
178
292
|
const mockResponse = {
|
|
@@ -190,32 +304,38 @@ export class MessengerWidget extends BaseWidget {
|
|
|
190
304
|
}
|
|
191
305
|
} catch (error) {
|
|
192
306
|
console.error('[MessengerWidget] Failed to send message:', error);
|
|
193
|
-
// Could add error handling UI here
|
|
194
307
|
}
|
|
195
308
|
}
|
|
196
309
|
|
|
197
|
-
/**
|
|
198
|
-
* Handle incoming WebSocket message
|
|
199
|
-
*/
|
|
200
310
|
_handleWebSocketMessage(data) {
|
|
201
311
|
const { conversation_id, message } = data;
|
|
202
312
|
|
|
203
|
-
|
|
313
|
+
let attachments = [];
|
|
314
|
+
if (message.attachments) {
|
|
315
|
+
try {
|
|
316
|
+
attachments =
|
|
317
|
+
typeof message.attachments === 'string'
|
|
318
|
+
? JSON.parse(message.attachments)
|
|
319
|
+
: message.attachments;
|
|
320
|
+
} catch (e) {
|
|
321
|
+
// ignore
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
204
325
|
const localMessage = {
|
|
205
326
|
id: message.id,
|
|
206
327
|
content: message.content,
|
|
207
328
|
isOwn: message.sender_type === 'customer',
|
|
208
329
|
timestamp: message.created_at,
|
|
330
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
209
331
|
sender: {
|
|
210
332
|
name: message.sender_name || 'Support',
|
|
211
333
|
avatarUrl: message.sender_avatar || null,
|
|
212
334
|
},
|
|
213
335
|
};
|
|
214
336
|
|
|
215
|
-
// Add message to state
|
|
216
337
|
this.messengerState.addMessage(conversation_id, localMessage);
|
|
217
338
|
|
|
218
|
-
// Update unread count if panel is closed or viewing different conversation
|
|
219
339
|
if (
|
|
220
340
|
!this.messengerState.isOpen ||
|
|
221
341
|
this.messengerState.activeConversationId !== conversation_id
|
|
@@ -224,9 +344,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
224
344
|
}
|
|
225
345
|
}
|
|
226
346
|
|
|
227
|
-
/**
|
|
228
|
-
* Handle typing started event
|
|
229
|
-
*/
|
|
230
347
|
_handleTypingStarted(data) {
|
|
231
348
|
if (data.is_agent) {
|
|
232
349
|
this.messengerState._notify('typingStarted', {
|
|
@@ -236,18 +353,20 @@ export class MessengerWidget extends BaseWidget {
|
|
|
236
353
|
}
|
|
237
354
|
}
|
|
238
355
|
|
|
239
|
-
/**
|
|
240
|
-
* Handle typing stopped event
|
|
241
|
-
*/
|
|
242
356
|
_handleTypingStopped(data) {
|
|
243
357
|
this.messengerState._notify('typingStopped', {
|
|
244
358
|
conversationId: data.conversation_id,
|
|
245
359
|
});
|
|
246
360
|
}
|
|
247
361
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
362
|
+
_handleConversationClosed(data) {
|
|
363
|
+
const conversationId =
|
|
364
|
+
data?.conversation_id || data?.id || data?.conversation?.id;
|
|
365
|
+
if (!conversationId) return;
|
|
366
|
+
|
|
367
|
+
this.messengerState.updateConversation(conversationId, { status: 'closed' });
|
|
368
|
+
}
|
|
369
|
+
|
|
251
370
|
async _updateUnreadCount() {
|
|
252
371
|
try {
|
|
253
372
|
const response = await this.apiService.getUnreadCount();
|
|
@@ -262,9 +381,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
262
381
|
}
|
|
263
382
|
}
|
|
264
383
|
|
|
265
|
-
/**
|
|
266
|
-
* Initialize WebSocket connection
|
|
267
|
-
*/
|
|
268
384
|
_initWebSocket() {
|
|
269
385
|
if (this.wsService) {
|
|
270
386
|
this.wsService.disconnect();
|
|
@@ -277,7 +393,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
277
393
|
mock: this.apiService.mock,
|
|
278
394
|
});
|
|
279
395
|
|
|
280
|
-
// Subscribe to WebSocket events
|
|
281
396
|
this._wsUnsubscribers.push(
|
|
282
397
|
this.wsService.on('message', this._handleWebSocketMessage)
|
|
283
398
|
);
|
|
@@ -287,9 +402,17 @@ export class MessengerWidget extends BaseWidget {
|
|
|
287
402
|
this._wsUnsubscribers.push(
|
|
288
403
|
this.wsService.on('typing_stopped', this._handleTypingStopped)
|
|
289
404
|
);
|
|
405
|
+
this._wsUnsubscribers.push(
|
|
406
|
+
this.wsService.on('conversation_closed', this._handleConversationClosed)
|
|
407
|
+
);
|
|
290
408
|
this._wsUnsubscribers.push(
|
|
291
409
|
this.wsService.on('connected', () => {
|
|
292
410
|
console.log('[MessengerWidget] WebSocket connected');
|
|
411
|
+
if (this.messengerState.activeConversationId) {
|
|
412
|
+
this.wsService.send('conversation:subscribe', {
|
|
413
|
+
conversation_id: this.messengerState.activeConversationId,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
293
416
|
})
|
|
294
417
|
);
|
|
295
418
|
this._wsUnsubscribers.push(
|
|
@@ -298,34 +421,21 @@ export class MessengerWidget extends BaseWidget {
|
|
|
298
421
|
})
|
|
299
422
|
);
|
|
300
423
|
|
|
301
|
-
// Connect
|
|
302
424
|
this.wsService.connect();
|
|
303
425
|
}
|
|
304
426
|
|
|
305
|
-
/**
|
|
306
|
-
* Open the messenger panel
|
|
307
|
-
*/
|
|
308
427
|
open() {
|
|
309
428
|
this.messengerState.setOpen(true);
|
|
310
429
|
}
|
|
311
430
|
|
|
312
|
-
/**
|
|
313
|
-
* Close the messenger panel
|
|
314
|
-
*/
|
|
315
431
|
close() {
|
|
316
432
|
this.messengerState.setOpen(false);
|
|
317
433
|
}
|
|
318
434
|
|
|
319
|
-
/**
|
|
320
|
-
* Toggle the messenger panel
|
|
321
|
-
*/
|
|
322
435
|
toggle() {
|
|
323
436
|
this.messengerState.setOpen(!this.messengerState.isOpen);
|
|
324
437
|
}
|
|
325
438
|
|
|
326
|
-
/**
|
|
327
|
-
* Navigate to a specific view
|
|
328
|
-
*/
|
|
329
439
|
navigateTo(view) {
|
|
330
440
|
this.messengerState.setView(view);
|
|
331
441
|
if (!this.messengerState.isOpen) {
|
|
@@ -333,52 +443,31 @@ export class MessengerWidget extends BaseWidget {
|
|
|
333
443
|
}
|
|
334
444
|
}
|
|
335
445
|
|
|
336
|
-
/**
|
|
337
|
-
* Set conversations
|
|
338
|
-
*/
|
|
339
446
|
setConversations(conversations) {
|
|
340
447
|
this.messengerState.setConversations(conversations);
|
|
341
448
|
}
|
|
342
449
|
|
|
343
|
-
/**
|
|
344
|
-
* Add a message to a conversation
|
|
345
|
-
*/
|
|
346
450
|
addMessage(conversationId, message) {
|
|
347
451
|
this.messengerState.addMessage(conversationId, message);
|
|
348
452
|
}
|
|
349
453
|
|
|
350
|
-
/**
|
|
351
|
-
* Set help articles
|
|
352
|
-
*/
|
|
353
454
|
setHelpArticles(articles) {
|
|
354
455
|
this.messengerState.setHelpArticles(articles);
|
|
355
456
|
}
|
|
356
457
|
|
|
357
|
-
/**
|
|
358
|
-
* Set home changelog items
|
|
359
|
-
*/
|
|
360
458
|
setHomeChangelogItems(items) {
|
|
361
459
|
this.messengerState.setHomeChangelogItems(items);
|
|
362
460
|
}
|
|
363
461
|
|
|
364
|
-
/**
|
|
365
|
-
* Set changelog items
|
|
366
|
-
*/
|
|
367
462
|
setChangelogItems(items) {
|
|
368
463
|
this.messengerState.setChangelogItems(items);
|
|
369
464
|
}
|
|
370
465
|
|
|
371
|
-
/**
|
|
372
|
-
* Update unread count (for external updates)
|
|
373
|
-
*/
|
|
374
466
|
setUnreadCount(count) {
|
|
375
467
|
this.messengerState.unreadCount = count;
|
|
376
468
|
this.messengerState._notify('unreadCountChange', { count });
|
|
377
469
|
}
|
|
378
470
|
|
|
379
|
-
/**
|
|
380
|
-
* Get current state
|
|
381
|
-
*/
|
|
382
471
|
getState() {
|
|
383
472
|
return {
|
|
384
473
|
isOpen: this.messengerState.isOpen,
|
|
@@ -388,11 +477,7 @@ export class MessengerWidget extends BaseWidget {
|
|
|
388
477
|
};
|
|
389
478
|
}
|
|
390
479
|
|
|
391
|
-
/**
|
|
392
|
-
* Load initial data (mock or API)
|
|
393
|
-
*/
|
|
394
480
|
async loadInitialData() {
|
|
395
|
-
// Load conversations
|
|
396
481
|
try {
|
|
397
482
|
const conversations = await this._fetchConversations();
|
|
398
483
|
this.messengerState.setConversations(conversations);
|
|
@@ -400,7 +485,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
400
485
|
console.error('[MessengerWidget] Failed to load conversations:', error);
|
|
401
486
|
}
|
|
402
487
|
|
|
403
|
-
// Load help articles if enabled
|
|
404
488
|
if (this.messengerOptions.enableHelp) {
|
|
405
489
|
try {
|
|
406
490
|
const articles = await this._fetchHelpArticles();
|
|
@@ -410,7 +494,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
410
494
|
}
|
|
411
495
|
}
|
|
412
496
|
|
|
413
|
-
// Load changelog if enabled
|
|
414
497
|
if (this.messengerOptions.enableChangelog) {
|
|
415
498
|
try {
|
|
416
499
|
const { homeItems, changelogItems } = await this._fetchChangelog();
|
|
@@ -426,7 +509,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
426
509
|
try {
|
|
427
510
|
const response = await this.apiService.getConversations();
|
|
428
511
|
if (response.status && response.data) {
|
|
429
|
-
// Transform API response to local format
|
|
430
512
|
return response.data.map((conv) => ({
|
|
431
513
|
id: conv.id,
|
|
432
514
|
title:
|
|
@@ -457,7 +539,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
457
539
|
try {
|
|
458
540
|
const response = await this.apiService.getHelpCollections();
|
|
459
541
|
if (response.status && response.data) {
|
|
460
|
-
// Transform API response to local format
|
|
461
542
|
return response.data.map((collection) => ({
|
|
462
543
|
id: collection.id,
|
|
463
544
|
title: collection.title || collection.name,
|
|
@@ -475,28 +556,39 @@ export class MessengerWidget extends BaseWidget {
|
|
|
475
556
|
}
|
|
476
557
|
}
|
|
477
558
|
|
|
478
|
-
/**
|
|
479
|
-
* Fetch messages for a conversation
|
|
480
|
-
*/
|
|
481
559
|
async fetchMessages(conversationId) {
|
|
482
560
|
try {
|
|
483
561
|
const response = await this.apiService.getConversation(conversationId);
|
|
484
562
|
if (response.status && response.data) {
|
|
485
|
-
const messages = (response.data.messages || []).map((msg) =>
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
563
|
+
const messages = (response.data.messages || []).map((msg) => {
|
|
564
|
+
let attachments;
|
|
565
|
+
if (msg.attachments) {
|
|
566
|
+
try {
|
|
567
|
+
attachments =
|
|
568
|
+
typeof msg.attachments === 'string'
|
|
569
|
+
? JSON.parse(msg.attachments)
|
|
570
|
+
: msg.attachments;
|
|
571
|
+
} catch (e) {
|
|
572
|
+
// ignore
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
id: msg.id,
|
|
577
|
+
content: msg.content,
|
|
578
|
+
isOwn: msg.sender_type === 'customer',
|
|
579
|
+
timestamp: msg.created_at,
|
|
580
|
+
attachments:
|
|
581
|
+
attachments && attachments.length > 0 ? attachments : undefined,
|
|
582
|
+
sender: {
|
|
583
|
+
name:
|
|
584
|
+
msg.sender_name ||
|
|
585
|
+
(msg.sender_type === 'customer' ? 'You' : 'Support'),
|
|
586
|
+
avatarUrl: msg.sender_avatar || null,
|
|
587
|
+
},
|
|
588
|
+
};
|
|
589
|
+
});
|
|
497
590
|
this.messengerState.setMessages(conversationId, messages);
|
|
498
591
|
|
|
499
|
-
// Mark as read
|
|
500
592
|
await this.apiService.markConversationAsRead(conversationId);
|
|
501
593
|
this.messengerState.markAsRead(conversationId);
|
|
502
594
|
|
|
@@ -509,16 +601,30 @@ export class MessengerWidget extends BaseWidget {
|
|
|
509
601
|
}
|
|
510
602
|
}
|
|
511
603
|
|
|
512
|
-
|
|
513
|
-
* Start a new conversation
|
|
514
|
-
*/
|
|
515
|
-
async startNewConversation(message, subject = '') {
|
|
604
|
+
async startNewConversation(message, subject = '', pendingAttachments = []) {
|
|
516
605
|
try {
|
|
606
|
+
const uploadedAttachments =
|
|
607
|
+
await this._uploadPendingAttachments(pendingAttachments);
|
|
608
|
+
|
|
609
|
+
console.log('[MessengerWidget] Starting conversation...', {
|
|
610
|
+
message,
|
|
611
|
+
attachmentCount: uploadedAttachments.length,
|
|
612
|
+
hasSession: this.apiService.isSessionValid(),
|
|
613
|
+
sessionToken: this.apiService.sessionToken
|
|
614
|
+
? this.apiService.sessionToken.substring(0, 10) + '...'
|
|
615
|
+
: null,
|
|
616
|
+
baseURL: this.apiService.baseURL,
|
|
617
|
+
mock: this.apiService.mock,
|
|
618
|
+
});
|
|
619
|
+
|
|
517
620
|
const response = await this.apiService.startConversation({
|
|
518
621
|
message,
|
|
519
622
|
subject,
|
|
623
|
+
attachments: uploadedAttachments,
|
|
520
624
|
});
|
|
521
625
|
|
|
626
|
+
console.log('[MessengerWidget] Conversation response:', response);
|
|
627
|
+
|
|
522
628
|
if (response.status && response.data) {
|
|
523
629
|
const conv = response.data;
|
|
524
630
|
const newConversation = {
|
|
@@ -531,10 +637,8 @@ export class MessengerWidget extends BaseWidget {
|
|
|
531
637
|
status: 'open',
|
|
532
638
|
};
|
|
533
639
|
|
|
534
|
-
// Add to state
|
|
535
640
|
this.messengerState.addConversation(newConversation);
|
|
536
641
|
|
|
537
|
-
// Set initial message in messages cache
|
|
538
642
|
this.messengerState.setMessages(conv.id, [
|
|
539
643
|
{
|
|
540
644
|
id: 'msg_' + Date.now(),
|
|
@@ -544,7 +648,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
544
648
|
},
|
|
545
649
|
]);
|
|
546
650
|
|
|
547
|
-
// Navigate to chat
|
|
548
651
|
this.messengerState.setActiveConversation(conv.id);
|
|
549
652
|
this.messengerState.setView('chat');
|
|
550
653
|
|
|
@@ -557,20 +660,14 @@ export class MessengerWidget extends BaseWidget {
|
|
|
557
660
|
}
|
|
558
661
|
}
|
|
559
662
|
|
|
560
|
-
/**
|
|
561
|
-
* Send typing indicator
|
|
562
|
-
*/
|
|
563
663
|
async sendTypingIndicator(conversationId, isTyping) {
|
|
564
664
|
try {
|
|
565
665
|
await this.apiService.sendTypingIndicator(conversationId, isTyping);
|
|
566
666
|
} catch (error) {
|
|
567
|
-
//
|
|
667
|
+
// Silent fail
|
|
568
668
|
}
|
|
569
669
|
}
|
|
570
670
|
|
|
571
|
-
/**
|
|
572
|
-
* Check agent availability
|
|
573
|
-
*/
|
|
574
671
|
async checkAgentAvailability() {
|
|
575
672
|
try {
|
|
576
673
|
const response = await this.apiService.checkAgentsOnline();
|
|
@@ -578,6 +675,13 @@ export class MessengerWidget extends BaseWidget {
|
|
|
578
675
|
this.messengerState.agentsOnline = response.data.agents_online;
|
|
579
676
|
this.messengerState.onlineCount = response.data.online_count || 0;
|
|
580
677
|
this.messengerState.responseTime = response.data.response_time || '';
|
|
678
|
+
|
|
679
|
+
if (response.data.available_agents) {
|
|
680
|
+
this.messengerState.setTeamAvatarsFromAgents(
|
|
681
|
+
response.data.available_agents
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
581
685
|
this.messengerState._notify('availabilityUpdate', response.data);
|
|
582
686
|
return response.data;
|
|
583
687
|
}
|
|
@@ -667,11 +771,9 @@ export class MessengerWidget extends BaseWidget {
|
|
|
667
771
|
};
|
|
668
772
|
}
|
|
669
773
|
|
|
670
|
-
// Fetch changelogs from API
|
|
671
774
|
const response = await this.apiService.getChangelogs({ limit: 20 });
|
|
672
775
|
const changelogs = response.data || [];
|
|
673
776
|
|
|
674
|
-
// Map API response to expected format
|
|
675
777
|
const mappedItems = changelogs.map((item) => ({
|
|
676
778
|
id: item.id,
|
|
677
779
|
title: item.title,
|
|
@@ -689,19 +791,30 @@ export class MessengerWidget extends BaseWidget {
|
|
|
689
791
|
};
|
|
690
792
|
}
|
|
691
793
|
|
|
692
|
-
onMount() {
|
|
693
|
-
|
|
794
|
+
async onMount() {
|
|
795
|
+
const userContext = this.messengerState.userContext;
|
|
796
|
+
if (userContext?.email && userContext?.name) {
|
|
797
|
+
try {
|
|
798
|
+
await this.apiService.identifyContact({
|
|
799
|
+
name: userContext.name,
|
|
800
|
+
email: userContext.email,
|
|
801
|
+
});
|
|
802
|
+
console.log('[MessengerWidget] User identified successfully');
|
|
803
|
+
} catch (error) {
|
|
804
|
+
if (error?.code !== 'ALREADY_IDENTIFIED') {
|
|
805
|
+
console.warn('[MessengerWidget] Identification failed:', error);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
694
810
|
this.loadInitialData();
|
|
695
811
|
|
|
696
|
-
// Initialize WebSocket for real-time updates
|
|
697
812
|
if (this.apiService?.sessionToken) {
|
|
698
813
|
this._initWebSocket();
|
|
699
814
|
}
|
|
700
815
|
|
|
701
|
-
// Check agent availability
|
|
702
816
|
this.checkAgentAvailability();
|
|
703
817
|
|
|
704
|
-
// Periodically check availability (every 60 seconds)
|
|
705
818
|
this._availabilityInterval = setInterval(() => {
|
|
706
819
|
this.checkAgentAvailability();
|
|
707
820
|
}, 60000);
|
|
@@ -712,16 +825,13 @@ export class MessengerWidget extends BaseWidget {
|
|
|
712
825
|
this._stateUnsubscribe();
|
|
713
826
|
}
|
|
714
827
|
|
|
715
|
-
// Clean up WebSocket
|
|
716
828
|
if (this.wsService) {
|
|
717
829
|
this.wsService.disconnect();
|
|
718
830
|
}
|
|
719
831
|
|
|
720
|
-
// Clean up WebSocket event listeners
|
|
721
832
|
this._wsUnsubscribers.forEach((unsub) => unsub());
|
|
722
833
|
this._wsUnsubscribers = [];
|
|
723
834
|
|
|
724
|
-
// Clean up availability interval
|
|
725
835
|
if (this._availabilityInterval) {
|
|
726
836
|
clearInterval(this._availabilityInterval);
|
|
727
837
|
}
|
|
@@ -737,4 +847,4 @@ export class MessengerWidget extends BaseWidget {
|
|
|
737
847
|
this.onDestroy();
|
|
738
848
|
super.destroy();
|
|
739
849
|
}
|
|
740
|
-
}
|
|
850
|
+
}
|