@product7/feedback-sdk 1.2.4 → 1.2.6
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/README.md +176 -7
- package/dist/README.md +176 -7
- package/dist/feedback-sdk.js +8156 -5140
- 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/core/APIService.js +643 -0
- package/src/core/FeedbackSDK.js +115 -5
- package/src/core/WebSocketService.js +273 -0
- package/src/index.js +3 -0
- package/src/styles/messengerStyles.js +100 -0
- package/src/styles/styles.js +747 -1
- package/src/widgets/BaseWidget.js +96 -0
- package/src/widgets/ButtonWidget.js +93 -91
- package/src/widgets/ChangelogWidget.js +619 -0
- package/src/widgets/MessengerWidget.js +374 -89
- package/src/widgets/WidgetFactory.js +2 -0
- package/src/widgets/messenger/MessengerState.js +12 -0
- package/src/widgets/messenger/components/NavigationTabs.js +1 -1
- package/src/widgets/messenger/views/ChatView.js +121 -16
- package/src/widgets/messenger/views/ConversationsView.js +12 -11
- package/src/widgets/messenger/views/HomeView.js +23 -1
- package/types/index.d.ts +152 -123
|
@@ -10,6 +10,7 @@ import { ChatView } from './messenger/views/ChatView.js';
|
|
|
10
10
|
import { ConversationsView } from './messenger/views/ConversationsView.js';
|
|
11
11
|
import { HelpView } from './messenger/views/HelpView.js';
|
|
12
12
|
import { HomeView } from './messenger/views/HomeView.js';
|
|
13
|
+
import { WebSocketService } from '../core/WebSocketService.js';
|
|
13
14
|
|
|
14
15
|
export class MessengerWidget extends BaseWidget {
|
|
15
16
|
constructor(options) {
|
|
@@ -44,9 +45,14 @@ export class MessengerWidget extends BaseWidget {
|
|
|
44
45
|
|
|
45
46
|
this.launcher = null;
|
|
46
47
|
this.panel = null;
|
|
48
|
+
this.wsService = null;
|
|
49
|
+
this._wsUnsubscribers = [];
|
|
47
50
|
|
|
48
51
|
// Bind methods
|
|
49
52
|
this._handleOpenChange = this._handleOpenChange.bind(this);
|
|
53
|
+
this._handleWebSocketMessage = this._handleWebSocketMessage.bind(this);
|
|
54
|
+
this._handleTypingStarted = this._handleTypingStarted.bind(this);
|
|
55
|
+
this._handleTypingStopped = this._handleTypingStopped.bind(this);
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
_render() {
|
|
@@ -62,16 +68,23 @@ export class MessengerWidget extends BaseWidget {
|
|
|
62
68
|
});
|
|
63
69
|
container.appendChild(this.launcher.render());
|
|
64
70
|
|
|
65
|
-
// Create panel
|
|
71
|
+
// Create panel with all callbacks
|
|
66
72
|
this.panel = new MessengerPanel(this.messengerState, {
|
|
67
73
|
position: this.messengerOptions.position,
|
|
68
74
|
theme: this.messengerOptions.theme,
|
|
69
75
|
primaryColor: this.messengerOptions.primaryColor,
|
|
70
76
|
logoUrl: this.messengerOptions.logoUrl,
|
|
71
77
|
featuredContent: this.messengerOptions.featuredContent,
|
|
78
|
+
// Chat callbacks
|
|
72
79
|
onSendMessage:
|
|
73
80
|
this.messengerOptions.onSendMessage ||
|
|
74
81
|
this._handleSendMessage.bind(this),
|
|
82
|
+
onStartConversation: this._handleStartConversation.bind(this),
|
|
83
|
+
onTyping: this.sendTypingIndicator.bind(this),
|
|
84
|
+
// Conversation list callbacks
|
|
85
|
+
onSelectConversation: this._handleSelectConversation.bind(this),
|
|
86
|
+
onStartNewConversation: this._handleNewConversationClick.bind(this),
|
|
87
|
+
// Article/changelog callbacks
|
|
75
88
|
onArticleClick: this.messengerOptions.onArticleClick,
|
|
76
89
|
onChangelogClick: this.messengerOptions.onChangelogClick,
|
|
77
90
|
});
|
|
@@ -109,32 +122,181 @@ export class MessengerWidget extends BaseWidget {
|
|
|
109
122
|
}
|
|
110
123
|
}
|
|
111
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Handle starting a new conversation
|
|
127
|
+
*/
|
|
128
|
+
async _handleStartConversation(messageContent) {
|
|
129
|
+
try {
|
|
130
|
+
await this.startNewConversation(messageContent);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('[MessengerWidget] Failed to start conversation:', error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Handle selecting a conversation from the list
|
|
138
|
+
*/
|
|
139
|
+
async _handleSelectConversation(conversationId) {
|
|
140
|
+
try {
|
|
141
|
+
await this.fetchMessages(conversationId);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error('[MessengerWidget] Failed to fetch messages:', error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Handle clicking "new conversation" button
|
|
149
|
+
*/
|
|
150
|
+
_handleNewConversationClick() {
|
|
151
|
+
// View is already changed by ConversationsView
|
|
152
|
+
// This is for any additional setup needed
|
|
153
|
+
}
|
|
154
|
+
|
|
112
155
|
async _handleSendMessage(conversationId, message) {
|
|
113
|
-
//
|
|
156
|
+
// Emit event for external listeners
|
|
114
157
|
this.sdk.eventBus.emit('messenger:messageSent', {
|
|
115
158
|
widget: this,
|
|
116
159
|
conversationId,
|
|
117
160
|
message,
|
|
118
161
|
});
|
|
119
162
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
163
|
+
try {
|
|
164
|
+
// Send message through API
|
|
165
|
+
const response = await this.apiService.sendMessage(conversationId, {
|
|
166
|
+
content: message.content,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
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
|
+
console.log('[MessengerWidget] Message sent:', response.data.id);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// In mock mode, simulate an agent response after a delay
|
|
176
|
+
if (this.apiService?.mock) {
|
|
177
|
+
setTimeout(() => {
|
|
178
|
+
const mockResponse = {
|
|
179
|
+
id: 'msg_' + Date.now(),
|
|
180
|
+
content: "Thanks for your message! We'll get back to you soon.",
|
|
181
|
+
isOwn: false,
|
|
182
|
+
timestamp: new Date().toISOString(),
|
|
183
|
+
sender: {
|
|
184
|
+
name: 'Support Team',
|
|
185
|
+
avatarUrl: null,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
this.messengerState.addMessage(conversationId, mockResponse);
|
|
189
|
+
}, 1500);
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error('[MessengerWidget] Failed to send message:', error);
|
|
193
|
+
// Could add error handling UI here
|
|
135
194
|
}
|
|
136
195
|
}
|
|
137
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Handle incoming WebSocket message
|
|
199
|
+
*/
|
|
200
|
+
_handleWebSocketMessage(data) {
|
|
201
|
+
const { conversation_id, message } = data;
|
|
202
|
+
|
|
203
|
+
// Transform message to local format
|
|
204
|
+
const localMessage = {
|
|
205
|
+
id: message.id,
|
|
206
|
+
content: message.content,
|
|
207
|
+
isOwn: message.sender_type === 'customer',
|
|
208
|
+
timestamp: message.created_at,
|
|
209
|
+
sender: {
|
|
210
|
+
name: message.sender_name || 'Support',
|
|
211
|
+
avatarUrl: message.sender_avatar || null,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Add message to state
|
|
216
|
+
this.messengerState.addMessage(conversation_id, localMessage);
|
|
217
|
+
|
|
218
|
+
// Update unread count if panel is closed or viewing different conversation
|
|
219
|
+
if (!this.messengerState.isOpen || this.messengerState.activeConversationId !== conversation_id) {
|
|
220
|
+
this._updateUnreadCount();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Handle typing started event
|
|
226
|
+
*/
|
|
227
|
+
_handleTypingStarted(data) {
|
|
228
|
+
if (data.is_agent) {
|
|
229
|
+
this.messengerState._notify('typingStarted', {
|
|
230
|
+
conversationId: data.conversation_id,
|
|
231
|
+
userName: data.user_name,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Handle typing stopped event
|
|
238
|
+
*/
|
|
239
|
+
_handleTypingStopped(data) {
|
|
240
|
+
this.messengerState._notify('typingStopped', {
|
|
241
|
+
conversationId: data.conversation_id,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Update unread count from API
|
|
247
|
+
*/
|
|
248
|
+
async _updateUnreadCount() {
|
|
249
|
+
try {
|
|
250
|
+
const response = await this.apiService.getUnreadCount();
|
|
251
|
+
if (response.status && response.data) {
|
|
252
|
+
this.messengerState.unreadCount = response.data.unread_count || 0;
|
|
253
|
+
this.messengerState._notify('unreadCountChange', { count: this.messengerState.unreadCount });
|
|
254
|
+
}
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error('[MessengerWidget] Failed to get unread count:', error);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Initialize WebSocket connection
|
|
262
|
+
*/
|
|
263
|
+
_initWebSocket() {
|
|
264
|
+
if (this.wsService) {
|
|
265
|
+
this.wsService.disconnect();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
this.wsService = new WebSocketService({
|
|
269
|
+
baseURL: this.apiService.baseURL,
|
|
270
|
+
workspace: this.apiService.workspace,
|
|
271
|
+
sessionToken: this.apiService.sessionToken,
|
|
272
|
+
mock: this.apiService.mock,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Subscribe to WebSocket events
|
|
276
|
+
this._wsUnsubscribers.push(
|
|
277
|
+
this.wsService.on('message', this._handleWebSocketMessage)
|
|
278
|
+
);
|
|
279
|
+
this._wsUnsubscribers.push(
|
|
280
|
+
this.wsService.on('typing_started', this._handleTypingStarted)
|
|
281
|
+
);
|
|
282
|
+
this._wsUnsubscribers.push(
|
|
283
|
+
this.wsService.on('typing_stopped', this._handleTypingStopped)
|
|
284
|
+
);
|
|
285
|
+
this._wsUnsubscribers.push(
|
|
286
|
+
this.wsService.on('connected', () => {
|
|
287
|
+
console.log('[MessengerWidget] WebSocket connected');
|
|
288
|
+
})
|
|
289
|
+
);
|
|
290
|
+
this._wsUnsubscribers.push(
|
|
291
|
+
this.wsService.on('disconnected', () => {
|
|
292
|
+
console.log('[MessengerWidget] WebSocket disconnected');
|
|
293
|
+
})
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
// Connect
|
|
297
|
+
this.wsService.connect();
|
|
298
|
+
}
|
|
299
|
+
|
|
138
300
|
/**
|
|
139
301
|
* Open the messenger panel
|
|
140
302
|
*/
|
|
@@ -256,82 +418,159 @@ export class MessengerWidget extends BaseWidget {
|
|
|
256
418
|
}
|
|
257
419
|
|
|
258
420
|
async _fetchConversations() {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
},
|
|
280
|
-
];
|
|
421
|
+
try {
|
|
422
|
+
const response = await this.apiService.getConversations();
|
|
423
|
+
if (response.status && response.data) {
|
|
424
|
+
// Transform API response to local format
|
|
425
|
+
return response.data.map((conv) => ({
|
|
426
|
+
id: conv.id,
|
|
427
|
+
title: conv.subject || `Chat with ${conv.assigned_user?.name || 'Support'}`,
|
|
428
|
+
participants: conv.assigned_user
|
|
429
|
+
? [{ name: conv.assigned_user.name, avatarUrl: conv.assigned_user.avatar }]
|
|
430
|
+
: [{ name: 'Support', avatarUrl: null }],
|
|
431
|
+
lastMessage: conv.preview || conv.snippet || '',
|
|
432
|
+
lastMessageTime: conv.last_message_at,
|
|
433
|
+
unread: conv.unread || 0,
|
|
434
|
+
status: conv.status,
|
|
435
|
+
}));
|
|
436
|
+
}
|
|
437
|
+
return [];
|
|
438
|
+
} catch (error) {
|
|
439
|
+
console.error('[MessengerWidget] Failed to fetch conversations:', error);
|
|
440
|
+
return [];
|
|
281
441
|
}
|
|
282
|
-
|
|
283
|
-
// TODO: Implement API call
|
|
284
|
-
return [];
|
|
285
442
|
}
|
|
286
443
|
|
|
287
444
|
async _fetchHelpArticles() {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
445
|
+
try {
|
|
446
|
+
const response = await this.apiService.getHelpCollections();
|
|
447
|
+
if (response.status && response.data) {
|
|
448
|
+
// Transform API response to local format
|
|
449
|
+
return response.data.map((collection) => ({
|
|
450
|
+
id: collection.id,
|
|
451
|
+
title: collection.title || collection.name,
|
|
452
|
+
description: collection.description || '',
|
|
453
|
+
articleCount: collection.article_count || collection.articleCount || 0,
|
|
454
|
+
icon: collection.icon || 'ph-book-open',
|
|
455
|
+
url: collection.url || `#/help/${collection.slug || collection.id}`,
|
|
456
|
+
}));
|
|
457
|
+
}
|
|
458
|
+
return [];
|
|
459
|
+
} catch (error) {
|
|
460
|
+
console.error('[MessengerWidget] Failed to fetch help articles:', error);
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Fetch messages for a conversation
|
|
467
|
+
*/
|
|
468
|
+
async fetchMessages(conversationId) {
|
|
469
|
+
try {
|
|
470
|
+
const response = await this.apiService.getConversation(conversationId);
|
|
471
|
+
if (response.status && response.data) {
|
|
472
|
+
const messages = (response.data.messages || []).map((msg) => ({
|
|
473
|
+
id: msg.id,
|
|
474
|
+
content: msg.content,
|
|
475
|
+
isOwn: msg.sender_type === 'customer',
|
|
476
|
+
timestamp: msg.created_at,
|
|
477
|
+
sender: {
|
|
478
|
+
name: msg.sender_name || (msg.sender_type === 'customer' ? 'You' : 'Support'),
|
|
479
|
+
avatarUrl: msg.sender_avatar || null,
|
|
480
|
+
},
|
|
481
|
+
}));
|
|
482
|
+
this.messengerState.setMessages(conversationId, messages);
|
|
483
|
+
|
|
484
|
+
// Mark as read
|
|
485
|
+
await this.apiService.markConversationAsRead(conversationId);
|
|
486
|
+
this.messengerState.markAsRead(conversationId);
|
|
487
|
+
|
|
488
|
+
return messages;
|
|
489
|
+
}
|
|
490
|
+
return [];
|
|
491
|
+
} catch (error) {
|
|
492
|
+
console.error('[MessengerWidget] Failed to fetch messages:', error);
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Start a new conversation
|
|
499
|
+
*/
|
|
500
|
+
async startNewConversation(message, subject = '') {
|
|
501
|
+
try {
|
|
502
|
+
const response = await this.apiService.startConversation({
|
|
503
|
+
message,
|
|
504
|
+
subject,
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (response.status && response.data) {
|
|
508
|
+
const conv = response.data;
|
|
509
|
+
const newConversation = {
|
|
510
|
+
id: conv.id,
|
|
511
|
+
title: conv.subject || 'New conversation',
|
|
512
|
+
participants: [{ name: 'Support', avatarUrl: null }],
|
|
513
|
+
lastMessage: message,
|
|
514
|
+
lastMessageTime: conv.created_at || new Date().toISOString(),
|
|
515
|
+
unread: 0,
|
|
516
|
+
status: 'open',
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// Add to state
|
|
520
|
+
this.messengerState.addConversation(newConversation);
|
|
521
|
+
|
|
522
|
+
// Set initial message in messages cache
|
|
523
|
+
this.messengerState.setMessages(conv.id, [
|
|
524
|
+
{
|
|
525
|
+
id: 'msg_' + Date.now(),
|
|
526
|
+
content: message,
|
|
527
|
+
isOwn: true,
|
|
528
|
+
timestamp: new Date().toISOString(),
|
|
529
|
+
},
|
|
530
|
+
]);
|
|
531
|
+
|
|
532
|
+
// Navigate to chat
|
|
533
|
+
this.messengerState.setActiveConversation(conv.id);
|
|
534
|
+
this.messengerState.setView('chat');
|
|
535
|
+
|
|
536
|
+
return conv;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
} catch (error) {
|
|
540
|
+
console.error('[MessengerWidget] Failed to start conversation:', error);
|
|
541
|
+
return null;
|
|
331
542
|
}
|
|
543
|
+
}
|
|
332
544
|
|
|
333
|
-
|
|
334
|
-
|
|
545
|
+
/**
|
|
546
|
+
* Send typing indicator
|
|
547
|
+
*/
|
|
548
|
+
async sendTypingIndicator(conversationId, isTyping) {
|
|
549
|
+
try {
|
|
550
|
+
await this.apiService.sendTypingIndicator(conversationId, isTyping);
|
|
551
|
+
} catch (error) {
|
|
552
|
+
// Silently fail - typing indicators are not critical
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Check agent availability
|
|
558
|
+
*/
|
|
559
|
+
async checkAgentAvailability() {
|
|
560
|
+
try {
|
|
561
|
+
const response = await this.apiService.checkAgentsOnline();
|
|
562
|
+
if (response.status && response.data) {
|
|
563
|
+
this.messengerState.agentsOnline = response.data.agents_online;
|
|
564
|
+
this.messengerState.onlineCount = response.data.online_count || 0;
|
|
565
|
+
this.messengerState.responseTime = response.data.response_time || '';
|
|
566
|
+
this.messengerState._notify('availabilityUpdate', response.data);
|
|
567
|
+
return response.data;
|
|
568
|
+
}
|
|
569
|
+
return { agents_online: false, online_count: 0 };
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.error('[MessengerWidget] Failed to check availability:', error);
|
|
572
|
+
return { agents_online: false, online_count: 0 };
|
|
573
|
+
}
|
|
335
574
|
}
|
|
336
575
|
|
|
337
576
|
async _fetchChangelog() {
|
|
@@ -414,19 +653,64 @@ export class MessengerWidget extends BaseWidget {
|
|
|
414
653
|
};
|
|
415
654
|
}
|
|
416
655
|
|
|
417
|
-
//
|
|
418
|
-
|
|
656
|
+
// Fetch changelogs from API
|
|
657
|
+
const response = await this.apiService.getChangelogs({ limit: 20 });
|
|
658
|
+
const changelogs = response.data || [];
|
|
659
|
+
|
|
660
|
+
// Map API response to expected format
|
|
661
|
+
const mappedItems = changelogs.map((item) => ({
|
|
662
|
+
id: item.id,
|
|
663
|
+
title: item.title,
|
|
664
|
+
description: item.excerpt || item.description || '',
|
|
665
|
+
tags: item.labels ? item.labels.map((label) => label.name) : [],
|
|
666
|
+
coverImage: item.cover_image || null,
|
|
667
|
+
coverText: null,
|
|
668
|
+
publishedAt: item.published_at,
|
|
669
|
+
url: item.slug ? `/changelog/${item.slug}` : '#',
|
|
670
|
+
}));
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
homeItems: mappedItems.slice(0, 3),
|
|
674
|
+
changelogItems: mappedItems,
|
|
675
|
+
};
|
|
419
676
|
}
|
|
420
677
|
|
|
421
678
|
onMount() {
|
|
422
679
|
// Load initial data after mounting
|
|
423
680
|
this.loadInitialData();
|
|
681
|
+
|
|
682
|
+
// Initialize WebSocket for real-time updates
|
|
683
|
+
if (this.apiService?.sessionToken) {
|
|
684
|
+
this._initWebSocket();
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Check agent availability
|
|
688
|
+
this.checkAgentAvailability();
|
|
689
|
+
|
|
690
|
+
// Periodically check availability (every 60 seconds)
|
|
691
|
+
this._availabilityInterval = setInterval(() => {
|
|
692
|
+
this.checkAgentAvailability();
|
|
693
|
+
}, 60000);
|
|
424
694
|
}
|
|
425
695
|
|
|
426
696
|
onDestroy() {
|
|
427
697
|
if (this._stateUnsubscribe) {
|
|
428
698
|
this._stateUnsubscribe();
|
|
429
699
|
}
|
|
700
|
+
|
|
701
|
+
// Clean up WebSocket
|
|
702
|
+
if (this.wsService) {
|
|
703
|
+
this.wsService.disconnect();
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Clean up WebSocket event listeners
|
|
707
|
+
this._wsUnsubscribers.forEach((unsub) => unsub());
|
|
708
|
+
this._wsUnsubscribers = [];
|
|
709
|
+
|
|
710
|
+
// Clean up availability interval
|
|
711
|
+
if (this._availabilityInterval) {
|
|
712
|
+
clearInterval(this._availabilityInterval);
|
|
713
|
+
}
|
|
430
714
|
}
|
|
431
715
|
|
|
432
716
|
destroy() {
|
|
@@ -436,6 +720,7 @@ export class MessengerWidget extends BaseWidget {
|
|
|
436
720
|
if (this.panel) {
|
|
437
721
|
this.panel.destroy();
|
|
438
722
|
}
|
|
723
|
+
this.onDestroy();
|
|
439
724
|
super.destroy();
|
|
440
725
|
}
|
|
441
726
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SDKError } from '../utils/errors.js';
|
|
2
2
|
import { ButtonWidget } from './ButtonWidget.js';
|
|
3
|
+
import { ChangelogWidget } from './ChangelogWidget.js';
|
|
3
4
|
import { InlineWidget } from './InlineWidget.js';
|
|
4
5
|
import { MessengerWidget } from './MessengerWidget.js';
|
|
5
6
|
import { SurveyWidget } from './SurveyWidget.js';
|
|
@@ -12,6 +13,7 @@ export class WidgetFactory {
|
|
|
12
13
|
['inline', InlineWidget],
|
|
13
14
|
['survey', SurveyWidget],
|
|
14
15
|
['messenger', MessengerWidget],
|
|
16
|
+
['changelog', ChangelogWidget],
|
|
15
17
|
]);
|
|
16
18
|
|
|
17
19
|
static register(type, WidgetClass) {
|
|
@@ -32,6 +32,18 @@ export class MessengerState {
|
|
|
32
32
|
this.enableHelp = options.enableHelp !== false;
|
|
33
33
|
this.enableChangelog = options.enableChangelog !== false;
|
|
34
34
|
|
|
35
|
+
// Agent availability
|
|
36
|
+
this.agentsOnline = false;
|
|
37
|
+
this.onlineCount = 0;
|
|
38
|
+
this.responseTime = 'Usually replies within a few minutes';
|
|
39
|
+
|
|
40
|
+
// Typing indicators
|
|
41
|
+
this.typingUsers = {}; // { conversationId: { userName, timestamp } }
|
|
42
|
+
|
|
43
|
+
// Loading states
|
|
44
|
+
this.isLoading = false;
|
|
45
|
+
this.isLoadingMessages = false;
|
|
46
|
+
|
|
35
47
|
// Listeners
|
|
36
48
|
this._listeners = new Set();
|
|
37
49
|
}
|
|
@@ -108,7 +108,7 @@ export class NavigationTabs {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
_getHomeIcon() {
|
|
111
|
-
return `<i class="ph-duotone ph-house
|
|
111
|
+
return `<i class="ph-duotone ph-house" style="font-size: 24px;"></i>`;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
_getMessagesIcon() {
|