@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.
@@ -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
- // Article/changelog callbacks
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
- * Handle starting a new conversation
127
- */
128
- async _handleStartConversation(messageContent) {
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
- await this.startNewConversation(messageContent);
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
- // View is already changed by ConversationsView
152
- // This is for any additional setup needed
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 _handleSendMessage(conversationId, message) {
156
- // Emit event for external listeners
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
- // Send message through API
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
- // Transform message to local format
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
- * Update unread count from API
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
- id: msg.id,
487
- content: msg.content,
488
- isOwn: msg.sender_type === 'customer',
489
- timestamp: msg.created_at,
490
- sender: {
491
- name:
492
- msg.sender_name ||
493
- (msg.sender_type === 'customer' ? 'You' : 'Support'),
494
- avatarUrl: msg.sender_avatar || null,
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
- // Silently fail - typing indicators are not critical
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
- // Load initial data after mounting
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
+ }