@v-tilt/browser 1.4.2 → 1.4.4
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/array.full.js +1 -1
- package/dist/array.full.js.map +1 -1
- package/dist/array.js +1 -1
- package/dist/array.js.map +1 -1
- package/dist/array.no-external.js +1 -1
- package/dist/array.no-external.js.map +1 -1
- package/dist/chat.js +1 -1
- package/dist/chat.js.map +1 -1
- package/dist/extensions/chat/chat-wrapper.d.ts +1 -1
- package/dist/extensions/chat/chat.d.ts +3 -33
- package/dist/external-scripts-loader.js +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/storage.d.ts +68 -51
- package/dist/user-manager.d.ts +15 -1
- package/lib/entrypoints/external-scripts-loader.js +1 -1
- package/lib/extensions/chat/chat-wrapper.d.ts +1 -1
- package/lib/extensions/chat/chat-wrapper.js +12 -6
- package/lib/extensions/chat/chat.d.ts +3 -33
- package/lib/extensions/chat/chat.js +102 -229
- package/lib/storage.d.ts +68 -51
- package/lib/storage.js +306 -219
- package/lib/user-manager.d.ts +15 -1
- package/lib/user-manager.js +38 -6
- package/lib/vtilt.js +6 -2
- package/package.json +1 -1
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Lazy
|
|
4
|
-
*
|
|
5
|
-
* The actual chat widget implementation that is loaded on demand.
|
|
6
|
-
* This file is bundled into chat.js and loaded when chat is enabled.
|
|
7
|
-
*
|
|
8
|
-
* Uses Ably for real-time messaging.
|
|
3
|
+
* Chat Widget - Lazy loaded chat implementation using Ably for real-time messaging.
|
|
9
4
|
*/
|
|
10
5
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
6
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -51,13 +46,11 @@ class LazyLoadedChat {
|
|
|
51
46
|
this._typingCallbacks = [];
|
|
52
47
|
this._connectionCallbacks = [];
|
|
53
48
|
// Timers
|
|
54
|
-
this._typingTimeout = null;
|
|
55
49
|
this._typingDebounce = null;
|
|
56
50
|
this._isUserTyping = false;
|
|
57
51
|
// Read tracking - initial position when widget opens (to show unread indicators)
|
|
58
52
|
this._initialUserReadAt = null;
|
|
59
53
|
this._isMarkingRead = false;
|
|
60
|
-
this._previousView = "list";
|
|
61
54
|
this._instance = instance;
|
|
62
55
|
this._config = {
|
|
63
56
|
enabled: true,
|
|
@@ -86,7 +79,6 @@ class LazyLoadedChat {
|
|
|
86
79
|
// Initialize UI
|
|
87
80
|
this._createUI();
|
|
88
81
|
this._attachEventListeners();
|
|
89
|
-
console.info(`${LOGGER_PREFIX} initialized`);
|
|
90
82
|
}
|
|
91
83
|
// ============================================================================
|
|
92
84
|
// Public API - State (LazyLoadedChatInterface)
|
|
@@ -125,8 +117,9 @@ class LazyLoadedChat {
|
|
|
125
117
|
// ============================================================================
|
|
126
118
|
open() {
|
|
127
119
|
var _a;
|
|
128
|
-
if (this._state.isOpen)
|
|
120
|
+
if (this._state.isOpen) {
|
|
129
121
|
return;
|
|
122
|
+
}
|
|
130
123
|
this._state.isOpen = true;
|
|
131
124
|
this._updateUI();
|
|
132
125
|
// Add opening animation
|
|
@@ -138,17 +131,15 @@ class LazyLoadedChat {
|
|
|
138
131
|
$page_url: (_a = globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.location) === null || _a === void 0 ? void 0 : _a.href,
|
|
139
132
|
$trigger: "api",
|
|
140
133
|
});
|
|
141
|
-
//
|
|
142
|
-
// Only fetch channels if not already loaded
|
|
134
|
+
// Fetch channels if not already loaded
|
|
143
135
|
if (this._state.channels.length === 0) {
|
|
144
136
|
this.getChannels();
|
|
145
137
|
}
|
|
146
|
-
// NOTE: Ably connection is now established only when entering conversation view
|
|
147
|
-
// This saves connection minutes when user is just browsing the channel list
|
|
148
138
|
}
|
|
149
139
|
close() {
|
|
150
|
-
if (!this._state.isOpen)
|
|
140
|
+
if (!this._state.isOpen) {
|
|
151
141
|
return;
|
|
142
|
+
}
|
|
152
143
|
const timeOpen = this._getTimeOpen();
|
|
153
144
|
// Add closing animation
|
|
154
145
|
if (this._widget) {
|
|
@@ -168,7 +159,6 @@ class LazyLoadedChat {
|
|
|
168
159
|
$time_open_seconds: timeOpen,
|
|
169
160
|
$messages_sent: this._state.messages.filter((m) => m.sender_type === "user").length,
|
|
170
161
|
});
|
|
171
|
-
// Disconnect Ably when widget closes to save connection minutes
|
|
172
162
|
this._disconnectRealtime();
|
|
173
163
|
}
|
|
174
164
|
toggle() {
|
|
@@ -190,9 +180,6 @@ class LazyLoadedChat {
|
|
|
190
180
|
// ============================================================================
|
|
191
181
|
// Public API - Channel Management (Multi-channel support)
|
|
192
182
|
// ============================================================================
|
|
193
|
-
/**
|
|
194
|
-
* Fetch/refresh the list of user's channels
|
|
195
|
-
*/
|
|
196
183
|
async getChannels() {
|
|
197
184
|
this._state.isLoading = true;
|
|
198
185
|
this._updateUI();
|
|
@@ -214,9 +201,6 @@ class LazyLoadedChat {
|
|
|
214
201
|
this._updateUI();
|
|
215
202
|
}
|
|
216
203
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Select a channel and load its messages
|
|
219
|
-
*/
|
|
220
204
|
async selectChannel(channelId) {
|
|
221
205
|
this._state.isLoading = true;
|
|
222
206
|
this._updateUI();
|
|
@@ -228,13 +212,10 @@ class LazyLoadedChat {
|
|
|
228
212
|
this._state.channel = response.channel;
|
|
229
213
|
this._state.messages = response.messages || [];
|
|
230
214
|
this._state.currentView = "conversation";
|
|
231
|
-
// Initialize read cursors from channel
|
|
232
215
|
this._state.agentLastReadAt =
|
|
233
216
|
response.channel.agent_last_read_at || null;
|
|
234
217
|
this._initialUserReadAt = response.channel.user_last_read_at || null;
|
|
235
|
-
// Connect to Ably for this channel
|
|
236
218
|
this._connectRealtime();
|
|
237
|
-
// Auto-mark unread messages as read if widget is open
|
|
238
219
|
if (this._state.isOpen) {
|
|
239
220
|
this._autoMarkAsRead();
|
|
240
221
|
}
|
|
@@ -248,9 +229,6 @@ class LazyLoadedChat {
|
|
|
248
229
|
this._updateUI();
|
|
249
230
|
}
|
|
250
231
|
}
|
|
251
|
-
/**
|
|
252
|
-
* Create a new channel and enter it
|
|
253
|
-
*/
|
|
254
232
|
async createChannel() {
|
|
255
233
|
var _a;
|
|
256
234
|
this._state.isLoading = true;
|
|
@@ -268,11 +246,9 @@ class LazyLoadedChat {
|
|
|
268
246
|
this._state.channel = response.channel;
|
|
269
247
|
this._state.messages = response.messages || [];
|
|
270
248
|
this._state.currentView = "conversation";
|
|
271
|
-
// Initialize read cursors
|
|
272
249
|
this._state.agentLastReadAt =
|
|
273
250
|
response.channel.agent_last_read_at || null;
|
|
274
251
|
this._initialUserReadAt = response.channel.user_last_read_at || null;
|
|
275
|
-
// Add new channel to the list
|
|
276
252
|
const newChannelSummary = {
|
|
277
253
|
id: response.channel.id,
|
|
278
254
|
status: response.channel.status,
|
|
@@ -285,13 +261,11 @@ class LazyLoadedChat {
|
|
|
285
261
|
created_at: response.channel.created_at,
|
|
286
262
|
};
|
|
287
263
|
this._state.channels.unshift(newChannelSummary);
|
|
288
|
-
// Track channel started
|
|
289
264
|
this._trackEvent(types_1.CHAT_EVENTS.STARTED, {
|
|
290
265
|
$channel_id: response.channel.id,
|
|
291
266
|
$initiated_by: "user",
|
|
292
267
|
$ai_mode: response.channel.ai_mode,
|
|
293
268
|
});
|
|
294
|
-
// Connect to Ably for this channel
|
|
295
269
|
this._connectRealtime();
|
|
296
270
|
}
|
|
297
271
|
}
|
|
@@ -303,13 +277,8 @@ class LazyLoadedChat {
|
|
|
303
277
|
this._updateUI();
|
|
304
278
|
}
|
|
305
279
|
}
|
|
306
|
-
/**
|
|
307
|
-
* Go back to channel list from conversation view
|
|
308
|
-
*/
|
|
309
280
|
goToChannelList() {
|
|
310
|
-
// Disconnect Ably when leaving conversation to save connection minutes
|
|
311
281
|
this._disconnectRealtime();
|
|
312
|
-
// Update the channel in the list with latest data
|
|
313
282
|
if (this._state.channel) {
|
|
314
283
|
const channelIndex = this._state.channels.findIndex((ch) => { var _a; return ch.id === ((_a = this._state.channel) === null || _a === void 0 ? void 0 : _a.id); });
|
|
315
284
|
if (channelIndex !== -1) {
|
|
@@ -322,13 +291,13 @@ class LazyLoadedChat {
|
|
|
322
291
|
? this._state.messages[this._state.messages.length - 1].content.substring(0, 100)
|
|
323
292
|
: this._state.channels[channelIndex].last_message_preview,
|
|
324
293
|
last_message_sender: this._state.messages.length > 0
|
|
325
|
-
? this._state.messages[this._state.messages.length - 1]
|
|
294
|
+
? this._state.messages[this._state.messages.length - 1]
|
|
295
|
+
.sender_type
|
|
326
296
|
: this._state.channels[channelIndex].last_message_sender,
|
|
327
297
|
unread_count: 0, // We just viewed it
|
|
328
298
|
};
|
|
329
299
|
}
|
|
330
300
|
}
|
|
331
|
-
// Clear current channel state
|
|
332
301
|
this._state.channel = null;
|
|
333
302
|
this._state.messages = [];
|
|
334
303
|
this._state.currentView = "list";
|
|
@@ -341,8 +310,9 @@ class LazyLoadedChat {
|
|
|
341
310
|
// ============================================================================
|
|
342
311
|
async sendMessage(content) {
|
|
343
312
|
var _a, _b, _c, _d;
|
|
344
|
-
if (!content.trim())
|
|
313
|
+
if (!content.trim()) {
|
|
345
314
|
return;
|
|
315
|
+
}
|
|
346
316
|
// Ensure we're in conversation view with a channel
|
|
347
317
|
if (!this._state.channel || this._state.currentView !== "conversation") {
|
|
348
318
|
console.error(`${LOGGER_PREFIX} Cannot send message: not in conversation view`);
|
|
@@ -383,9 +353,6 @@ class LazyLoadedChat {
|
|
|
383
353
|
if (index !== -1 && (response === null || response === void 0 ? void 0 : response.message)) {
|
|
384
354
|
this._state.messages[index] = response.message;
|
|
385
355
|
}
|
|
386
|
-
// Note: AI response will come through Ably channel if AI mode is enabled
|
|
387
|
-
// No need to handle it here since server publishes to Ably
|
|
388
|
-
// Track event
|
|
389
356
|
this._trackEvent(types_1.CHAT_EVENTS.MESSAGE_SENT, {
|
|
390
357
|
$channel_id: channelId,
|
|
391
358
|
$message_id: (_b = response === null || response === void 0 ? void 0 : response.message) === null || _b === void 0 ? void 0 : _b.id,
|
|
@@ -406,28 +373,22 @@ class LazyLoadedChat {
|
|
|
406
373
|
markAsRead() {
|
|
407
374
|
this._autoMarkAsRead();
|
|
408
375
|
}
|
|
409
|
-
/**
|
|
410
|
-
* Automatically mark unread agent/AI messages as read
|
|
411
|
-
* Called when widget opens or new messages arrive while open
|
|
412
|
-
*/
|
|
413
376
|
_autoMarkAsRead() {
|
|
414
|
-
if (!this._state.channel)
|
|
377
|
+
if (!this._state.channel || this._isMarkingRead) {
|
|
415
378
|
return;
|
|
416
|
-
|
|
417
|
-
return; // Already in progress
|
|
418
|
-
// Get the latest message timestamp
|
|
379
|
+
}
|
|
419
380
|
const latestMessage = this._state.messages[this._state.messages.length - 1];
|
|
420
|
-
if (!latestMessage)
|
|
381
|
+
if (!latestMessage) {
|
|
421
382
|
return;
|
|
422
|
-
|
|
383
|
+
}
|
|
423
384
|
const hasUnreadAgentMessages = this._state.messages.some((m) => (m.sender_type === "agent" || m.sender_type === "ai") &&
|
|
424
385
|
!this._isMessageReadByUser(m.created_at));
|
|
425
|
-
if (!hasUnreadAgentMessages)
|
|
386
|
+
if (!hasUnreadAgentMessages) {
|
|
426
387
|
return;
|
|
388
|
+
}
|
|
427
389
|
this._state.unreadCount = 0;
|
|
428
390
|
this._isMarkingRead = true;
|
|
429
391
|
this._updateUI();
|
|
430
|
-
// API call to update read cursor with latest message timestamp
|
|
431
392
|
this._apiRequest(API.read, {
|
|
432
393
|
method: "POST",
|
|
433
394
|
body: JSON.stringify({
|
|
@@ -437,23 +398,19 @@ class LazyLoadedChat {
|
|
|
437
398
|
}),
|
|
438
399
|
})
|
|
439
400
|
.then(() => {
|
|
440
|
-
// Update initial read position after successful mark
|
|
441
401
|
this._initialUserReadAt = latestMessage.created_at;
|
|
442
402
|
this._isMarkingRead = false;
|
|
443
403
|
this._updateUI();
|
|
444
404
|
})
|
|
445
|
-
.catch((
|
|
446
|
-
console.error(`${LOGGER_PREFIX} Failed to mark as read:`, err);
|
|
405
|
+
.catch(() => {
|
|
447
406
|
this._isMarkingRead = false;
|
|
448
407
|
this._updateUI();
|
|
449
408
|
});
|
|
450
409
|
}
|
|
451
|
-
/**
|
|
452
|
-
* Check if a message has been read by the user (using initial cursor)
|
|
453
|
-
*/
|
|
454
410
|
_isMessageReadByUser(messageCreatedAt) {
|
|
455
|
-
if (!this._initialUserReadAt)
|
|
411
|
+
if (!this._initialUserReadAt) {
|
|
456
412
|
return false;
|
|
413
|
+
}
|
|
457
414
|
return new Date(messageCreatedAt) <= new Date(this._initialUserReadAt);
|
|
458
415
|
}
|
|
459
416
|
// ============================================================================
|
|
@@ -463,46 +420,44 @@ class LazyLoadedChat {
|
|
|
463
420
|
this._messageCallbacks.push(callback);
|
|
464
421
|
return () => {
|
|
465
422
|
const index = this._messageCallbacks.indexOf(callback);
|
|
466
|
-
if (index > -1)
|
|
423
|
+
if (index > -1) {
|
|
467
424
|
this._messageCallbacks.splice(index, 1);
|
|
425
|
+
}
|
|
468
426
|
};
|
|
469
427
|
}
|
|
470
428
|
onTyping(callback) {
|
|
471
429
|
this._typingCallbacks.push(callback);
|
|
472
430
|
return () => {
|
|
473
431
|
const index = this._typingCallbacks.indexOf(callback);
|
|
474
|
-
if (index > -1)
|
|
432
|
+
if (index > -1) {
|
|
475
433
|
this._typingCallbacks.splice(index, 1);
|
|
434
|
+
}
|
|
476
435
|
};
|
|
477
436
|
}
|
|
478
437
|
onConnectionChange(callback) {
|
|
479
438
|
this._connectionCallbacks.push(callback);
|
|
480
439
|
return () => {
|
|
481
440
|
const index = this._connectionCallbacks.indexOf(callback);
|
|
482
|
-
if (index > -1)
|
|
441
|
+
if (index > -1) {
|
|
483
442
|
this._connectionCallbacks.splice(index, 1);
|
|
443
|
+
}
|
|
484
444
|
};
|
|
485
445
|
}
|
|
486
446
|
// ============================================================================
|
|
487
447
|
// Public API - Lifecycle
|
|
488
448
|
// ============================================================================
|
|
489
449
|
destroy() {
|
|
490
|
-
|
|
450
|
+
var _a;
|
|
491
451
|
this._disconnectRealtime();
|
|
492
|
-
|
|
493
|
-
if (this._typingTimeout)
|
|
494
|
-
clearTimeout(this._typingTimeout);
|
|
495
|
-
if (this._typingDebounce)
|
|
452
|
+
if (this._typingDebounce) {
|
|
496
453
|
clearTimeout(this._typingDebounce);
|
|
497
|
-
|
|
498
|
-
if (this._container
|
|
454
|
+
}
|
|
455
|
+
if ((_a = this._container) === null || _a === void 0 ? void 0 : _a.parentNode) {
|
|
499
456
|
this._container.parentNode.removeChild(this._container);
|
|
500
457
|
}
|
|
501
|
-
// Clear callbacks
|
|
502
458
|
this._messageCallbacks = [];
|
|
503
459
|
this._typingCallbacks = [];
|
|
504
460
|
this._connectionCallbacks = [];
|
|
505
|
-
console.info(`${LOGGER_PREFIX} destroyed`);
|
|
506
461
|
}
|
|
507
462
|
// ============================================================================
|
|
508
463
|
// Private - Ably Realtime Connection
|
|
@@ -527,7 +482,6 @@ class LazyLoadedChat {
|
|
|
527
482
|
this._connectionState = "error";
|
|
528
483
|
return;
|
|
529
484
|
}
|
|
530
|
-
// Create Ably client with token auth
|
|
531
485
|
this._ably = new ably_1.default.Realtime({
|
|
532
486
|
authCallback: async (_, callback) => {
|
|
533
487
|
var _a;
|
|
@@ -552,37 +506,25 @@ class LazyLoadedChat {
|
|
|
552
506
|
},
|
|
553
507
|
authMethod: "POST",
|
|
554
508
|
});
|
|
555
|
-
// Authenticate with initial token
|
|
556
509
|
await this._ably.auth.authorize(tokenResponse.tokenRequest);
|
|
557
|
-
// Get project ID from instance config
|
|
558
510
|
const config = this._instance.getConfig();
|
|
559
511
|
const projectId = config.projectId || this._extractProjectId(config.token || "");
|
|
560
|
-
// Subscribe to chat channel (Ably channel)
|
|
561
512
|
const ablyChannelName = `chat:${projectId}:${this._state.channel.id}`;
|
|
562
513
|
this._ablyChannel = this._ably.channels.get(ablyChannelName);
|
|
563
|
-
// Listen for new messages
|
|
564
514
|
this._ablyChannel.subscribe("message", (msg) => {
|
|
565
|
-
|
|
566
|
-
this._handleNewMessage(message);
|
|
567
|
-
});
|
|
568
|
-
// Subscribe to typing channel
|
|
569
|
-
const typingChannelName = `${ablyChannelName}:typing`;
|
|
570
|
-
this._typingChannel = this._ably.channels.get(typingChannelName);
|
|
571
|
-
this._typingChannel.subscribe("typing", (msg) => {
|
|
572
|
-
const event = msg.data;
|
|
573
|
-
this._handleTypingEvent(event);
|
|
515
|
+
this._handleNewMessage(msg.data);
|
|
574
516
|
});
|
|
575
|
-
// Subscribe to read cursor events
|
|
576
517
|
this._ablyChannel.subscribe("read", (msg) => {
|
|
577
|
-
|
|
578
|
-
|
|
518
|
+
this._handleReadCursorEvent(msg.data);
|
|
519
|
+
});
|
|
520
|
+
this._typingChannel = this._ably.channels.get(`${ablyChannelName}:typing`);
|
|
521
|
+
this._typingChannel.subscribe("typing", (msg) => {
|
|
522
|
+
this._handleTypingEvent(msg.data);
|
|
579
523
|
});
|
|
580
|
-
// Handle connection state changes
|
|
581
524
|
this._ably.connection.on("connected", () => {
|
|
582
525
|
this._connectionState = "connected";
|
|
583
526
|
this._state.isConnected = true;
|
|
584
527
|
this._notifyConnectionChange(true);
|
|
585
|
-
console.info(`${LOGGER_PREFIX} Connected to Ably`);
|
|
586
528
|
});
|
|
587
529
|
this._ably.connection.on("disconnected", () => {
|
|
588
530
|
this._connectionState = "disconnected";
|
|
@@ -594,7 +536,6 @@ class LazyLoadedChat {
|
|
|
594
536
|
this._state.isConnected = false;
|
|
595
537
|
this._notifyConnectionChange(false);
|
|
596
538
|
});
|
|
597
|
-
// Initial connection
|
|
598
539
|
this._ably.connect();
|
|
599
540
|
}
|
|
600
541
|
catch (error) {
|
|
@@ -624,14 +565,12 @@ class LazyLoadedChat {
|
|
|
624
565
|
return parts[0] || "";
|
|
625
566
|
}
|
|
626
567
|
_handleNewMessage(message) {
|
|
627
|
-
|
|
628
|
-
if (this._state.messages.some((m) => m.id === message.id))
|
|
568
|
+
if (this._state.messages.some((m) => m.id === message.id)) {
|
|
629
569
|
return;
|
|
630
|
-
|
|
631
|
-
//
|
|
570
|
+
}
|
|
571
|
+
// Skip own messages but replace temp message if present
|
|
632
572
|
if (message.sender_type === "user" &&
|
|
633
573
|
message.sender_id === this._distinctId) {
|
|
634
|
-
// But DO replace temp message with real one if present
|
|
635
574
|
const tempIndex = this._state.messages.findIndex((m) => m.id.startsWith("temp-") &&
|
|
636
575
|
m.content === message.content &&
|
|
637
576
|
m.sender_type === "user");
|
|
@@ -641,19 +580,14 @@ class LazyLoadedChat {
|
|
|
641
580
|
}
|
|
642
581
|
return;
|
|
643
582
|
}
|
|
644
|
-
// Add to messages
|
|
645
583
|
this._state.messages.push(message);
|
|
646
|
-
// Update unread count if from agent/AI
|
|
647
584
|
if (message.sender_type !== "user") {
|
|
648
585
|
if (!this._state.isOpen) {
|
|
649
586
|
this._state.unreadCount++;
|
|
650
587
|
}
|
|
651
588
|
else {
|
|
652
|
-
// Widget is open, auto-mark as read after a short delay
|
|
653
|
-
// to ensure UI updates first
|
|
654
589
|
setTimeout(() => this._autoMarkAsRead(), 100);
|
|
655
590
|
}
|
|
656
|
-
// Track received event
|
|
657
591
|
this._trackEvent(types_1.CHAT_EVENTS.MESSAGE_RECEIVED, {
|
|
658
592
|
$channel_id: message.channel_id,
|
|
659
593
|
$message_id: message.id,
|
|
@@ -661,32 +595,27 @@ class LazyLoadedChat {
|
|
|
661
595
|
$sender_type: message.sender_type,
|
|
662
596
|
});
|
|
663
597
|
}
|
|
664
|
-
// Clear typing indicator when message arrives
|
|
665
598
|
this._state.isTyping = false;
|
|
666
599
|
this._state.typingSender = null;
|
|
667
|
-
// Notify callbacks
|
|
668
600
|
this._messageCallbacks.forEach((cb) => cb(message));
|
|
669
601
|
this._updateUI();
|
|
670
602
|
}
|
|
671
603
|
_handleTypingEvent(event) {
|
|
672
|
-
|
|
673
|
-
if (event.sender_type === "user")
|
|
604
|
+
if (event.sender_type === "user") {
|
|
674
605
|
return;
|
|
606
|
+
}
|
|
675
607
|
const senderName = event.sender_name ||
|
|
676
608
|
(event.sender_type === "ai" ? "AI Assistant" : "Agent");
|
|
677
609
|
this._state.isTyping = event.is_typing;
|
|
678
610
|
this._state.typingSender = event.is_typing ? senderName : null;
|
|
679
|
-
// Notify callbacks
|
|
680
611
|
this._typingCallbacks.forEach((cb) => cb(event.is_typing, senderName));
|
|
681
612
|
this._updateUI();
|
|
682
613
|
}
|
|
683
614
|
_handleReadCursorEvent(event) {
|
|
684
|
-
|
|
685
|
-
if (event.reader_type !== "agent")
|
|
615
|
+
if (event.reader_type !== "agent") {
|
|
686
616
|
return;
|
|
687
|
-
|
|
617
|
+
}
|
|
688
618
|
this._state.agentLastReadAt = event.read_at;
|
|
689
|
-
// Update UI to show read status on user messages
|
|
690
619
|
this._updateUI();
|
|
691
620
|
}
|
|
692
621
|
_notifyConnectionChange(connected) {
|
|
@@ -696,59 +625,49 @@ class LazyLoadedChat {
|
|
|
696
625
|
// Private - UI
|
|
697
626
|
// ============================================================================
|
|
698
627
|
_createUI() {
|
|
699
|
-
if (!globals_1.document)
|
|
628
|
+
if (!globals_1.document) {
|
|
700
629
|
return;
|
|
701
|
-
|
|
630
|
+
}
|
|
702
631
|
this._container = globals_1.document.createElement("div");
|
|
703
632
|
this._container.id = "vtilt-chat-container";
|
|
704
633
|
this._container.setAttribute("style", this._getContainerStyles());
|
|
705
|
-
// Create bubble (launcher button)
|
|
706
634
|
this._bubble = globals_1.document.createElement("div");
|
|
707
635
|
this._bubble.id = "vtilt-chat-bubble";
|
|
708
636
|
this._bubble.innerHTML = this._getBubbleHTML();
|
|
709
637
|
this._bubble.setAttribute("style", this._getBubbleStyles());
|
|
710
638
|
this._container.appendChild(this._bubble);
|
|
711
|
-
// Create widget (chat window)
|
|
712
639
|
this._widget = globals_1.document.createElement("div");
|
|
713
640
|
this._widget.id = "vtilt-chat-widget";
|
|
714
641
|
this._widget.innerHTML = this._getWidgetHTML();
|
|
715
642
|
this._widget.setAttribute("style", this._getWidgetStyles());
|
|
716
643
|
this._container.appendChild(this._widget);
|
|
717
|
-
// Add to DOM
|
|
718
644
|
globals_1.document.body.appendChild(this._container);
|
|
719
645
|
}
|
|
720
646
|
_attachEventListeners() {
|
|
721
|
-
var _a, _b;
|
|
722
|
-
// Bubble click
|
|
647
|
+
var _a, _b, _c;
|
|
723
648
|
(_a = this._bubble) === null || _a === void 0 ? void 0 : _a.addEventListener("click", () => this.toggle());
|
|
724
|
-
|
|
725
|
-
const closeBtn = (_b = this._widget) === null || _b === void 0 ? void 0 : _b.querySelector(".vtilt-chat-close");
|
|
726
|
-
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.addEventListener("click", () => this.close());
|
|
727
|
-
// Note: Channel list and conversation listeners are attached dynamically
|
|
728
|
-
// in _attachChannelListListeners() and _attachConversationListeners()
|
|
649
|
+
(_c = (_b = this._widget) === null || _b === void 0 ? void 0 : _b.querySelector(".vtilt-chat-close")) === null || _c === void 0 ? void 0 : _c.addEventListener("click", () => this.close());
|
|
729
650
|
}
|
|
730
651
|
_handleUserTyping() {
|
|
731
|
-
|
|
732
|
-
if (!this._typingChannel)
|
|
652
|
+
if (!this._typingChannel) {
|
|
733
653
|
return;
|
|
734
|
-
|
|
654
|
+
}
|
|
735
655
|
if (!this._isUserTyping) {
|
|
736
656
|
this._isUserTyping = true;
|
|
737
657
|
this._sendTypingIndicator(true);
|
|
738
658
|
}
|
|
739
|
-
// Clear existing debounce timer
|
|
740
659
|
if (this._typingDebounce) {
|
|
741
660
|
clearTimeout(this._typingDebounce);
|
|
742
661
|
}
|
|
743
|
-
// Set timer to send typing stopped after 2 seconds of no input
|
|
744
662
|
this._typingDebounce = setTimeout(() => {
|
|
745
663
|
this._isUserTyping = false;
|
|
746
664
|
this._sendTypingIndicator(false);
|
|
747
665
|
}, 2000);
|
|
748
666
|
}
|
|
749
667
|
_sendTypingIndicator(isTyping) {
|
|
750
|
-
if (!this._typingChannel)
|
|
668
|
+
if (!this._typingChannel) {
|
|
751
669
|
return;
|
|
670
|
+
}
|
|
752
671
|
try {
|
|
753
672
|
this._typingChannel.publish("typing", {
|
|
754
673
|
sender_type: "user",
|
|
@@ -757,18 +676,18 @@ class LazyLoadedChat {
|
|
|
757
676
|
is_typing: isTyping,
|
|
758
677
|
});
|
|
759
678
|
}
|
|
760
|
-
catch (
|
|
761
|
-
|
|
679
|
+
catch (_a) {
|
|
680
|
+
// Silently fail
|
|
762
681
|
}
|
|
763
682
|
}
|
|
764
683
|
_handleSend() {
|
|
765
684
|
var _a;
|
|
766
685
|
const input = (_a = this._widget) === null || _a === void 0 ? void 0 : _a.querySelector(".vtilt-chat-input");
|
|
767
|
-
if (!input)
|
|
686
|
+
if (!input) {
|
|
768
687
|
return;
|
|
688
|
+
}
|
|
769
689
|
const content = input.value.trim();
|
|
770
690
|
if (content) {
|
|
771
|
-
// Stop typing indicator
|
|
772
691
|
if (this._isUserTyping) {
|
|
773
692
|
this._isUserTyping = false;
|
|
774
693
|
this._sendTypingIndicator(false);
|
|
@@ -782,8 +701,9 @@ class LazyLoadedChat {
|
|
|
782
701
|
}
|
|
783
702
|
}
|
|
784
703
|
_updateUI() {
|
|
785
|
-
if (!this._container || !this._widget || !this._bubble)
|
|
704
|
+
if (!this._container || !this._widget || !this._bubble) {
|
|
786
705
|
return;
|
|
706
|
+
}
|
|
787
707
|
// Update visibility
|
|
788
708
|
this._container.style.display = this._state.isVisible ? "block" : "none";
|
|
789
709
|
// Update widget open state
|
|
@@ -795,18 +715,9 @@ class LazyLoadedChat {
|
|
|
795
715
|
this._state.unreadCount > 0 ? "flex" : "none";
|
|
796
716
|
badge.textContent = String(this._state.unreadCount);
|
|
797
717
|
}
|
|
798
|
-
// Detect view change for animation
|
|
799
|
-
const viewChanged = this._previousView !== this._state.currentView;
|
|
800
|
-
const animationClass = viewChanged
|
|
801
|
-
? this._state.currentView === "conversation"
|
|
802
|
-
? "vtilt-view-enter-right"
|
|
803
|
-
: "vtilt-view-enter-left"
|
|
804
|
-
: "";
|
|
805
718
|
// Update content based on current view
|
|
806
719
|
const contentContainer = this._widget.querySelector(".vtilt-chat-content");
|
|
807
720
|
if (contentContainer) {
|
|
808
|
-
// Remove previous animation classes
|
|
809
|
-
contentContainer.classList.remove("vtilt-view-enter-right", "vtilt-view-enter-left");
|
|
810
721
|
if (this._state.currentView === "list") {
|
|
811
722
|
contentContainer.innerHTML = this._getChannelListHTML();
|
|
812
723
|
this._attachChannelListListeners();
|
|
@@ -814,18 +725,9 @@ class LazyLoadedChat {
|
|
|
814
725
|
else {
|
|
815
726
|
contentContainer.innerHTML = this._getConversationHTML();
|
|
816
727
|
this._attachConversationListeners();
|
|
817
|
-
// Render messages in conversation view
|
|
818
728
|
this._renderMessages();
|
|
819
729
|
}
|
|
820
|
-
// Add animation class if view changed
|
|
821
|
-
if (animationClass) {
|
|
822
|
-
// Force reflow to restart animation
|
|
823
|
-
void contentContainer.offsetWidth;
|
|
824
|
-
contentContainer.classList.add(animationClass);
|
|
825
|
-
}
|
|
826
730
|
}
|
|
827
|
-
// Update previous view
|
|
828
|
-
this._previousView = this._state.currentView;
|
|
829
731
|
// Update header based on view
|
|
830
732
|
this._updateHeader();
|
|
831
733
|
// Update loading state
|
|
@@ -851,8 +753,9 @@ class LazyLoadedChat {
|
|
|
851
753
|
_updateHeader() {
|
|
852
754
|
var _a, _b;
|
|
853
755
|
const header = (_a = this._widget) === null || _a === void 0 ? void 0 : _a.querySelector(".vtilt-chat-header");
|
|
854
|
-
if (!header)
|
|
756
|
+
if (!header) {
|
|
855
757
|
return;
|
|
758
|
+
}
|
|
856
759
|
const greeting = this._config.greeting || "Messages";
|
|
857
760
|
const primary = this._theme.primaryColor;
|
|
858
761
|
if (this._state.currentView === "list") {
|
|
@@ -979,7 +882,6 @@ class LazyLoadedChat {
|
|
|
979
882
|
justify-content: center;
|
|
980
883
|
padding: 48px 24px;
|
|
981
884
|
text-align: center;
|
|
982
|
-
animation: vtilt-fadein 0.4s ease;
|
|
983
885
|
">
|
|
984
886
|
<div style="
|
|
985
887
|
width: 72px;
|
|
@@ -990,14 +892,13 @@ class LazyLoadedChat {
|
|
|
990
892
|
display: flex;
|
|
991
893
|
align-items: center;
|
|
992
894
|
justify-content: center;
|
|
993
|
-
animation: vtilt-bubble-pop 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) 0.1s both;
|
|
994
895
|
">
|
|
995
896
|
<svg width="36" height="36" viewBox="0 0 24 24" fill="white">
|
|
996
897
|
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/>
|
|
997
898
|
</svg>
|
|
998
899
|
</div>
|
|
999
|
-
<div style="font-size: 18px; font-weight: 600; color: #000000; margin-bottom: 8px;
|
|
1000
|
-
<div style="font-size: 15px; color: #666666; margin-bottom: 28px; line-height: 1.5; max-width: 280px;
|
|
900
|
+
<div style="font-size: 18px; font-weight: 600; color: #000000; margin-bottom: 8px;">No conversations yet</div>
|
|
901
|
+
<div style="font-size: 15px; color: #666666; margin-bottom: 28px; line-height: 1.5; max-width: 280px;">Questions? We're here to help. Start a conversation with us.</div>
|
|
1001
902
|
<button class="vtilt-chat-new-channel" style="
|
|
1002
903
|
background: ${primary};
|
|
1003
904
|
color: white;
|
|
@@ -1013,7 +914,6 @@ class LazyLoadedChat {
|
|
|
1013
914
|
align-items: center;
|
|
1014
915
|
gap: 10px;
|
|
1015
916
|
box-shadow: 0 2px 8px rgba(123, 104, 238, 0.3);
|
|
1016
|
-
animation: vtilt-fadein 0.4s ease 0.25s both;
|
|
1017
917
|
">
|
|
1018
918
|
Send us a message
|
|
1019
919
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="white">
|
|
@@ -1024,7 +924,7 @@ class LazyLoadedChat {
|
|
|
1024
924
|
`;
|
|
1025
925
|
}
|
|
1026
926
|
const channelsHtml = this._state.channels
|
|
1027
|
-
.map((ch
|
|
927
|
+
.map((ch) => this._getChannelItemHTML(ch))
|
|
1028
928
|
.join("");
|
|
1029
929
|
return `
|
|
1030
930
|
<div style="flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;">
|
|
@@ -1061,13 +961,12 @@ class LazyLoadedChat {
|
|
|
1061
961
|
</div>
|
|
1062
962
|
`;
|
|
1063
963
|
}
|
|
1064
|
-
_getChannelItemHTML(channel
|
|
964
|
+
_getChannelItemHTML(channel) {
|
|
1065
965
|
const hasUnread = channel.unread_count > 0;
|
|
1066
966
|
const timeStr = this._formatRelativeTime(channel.last_message_at || channel.created_at);
|
|
1067
967
|
const preview = channel.last_message_preview || "No messages yet";
|
|
1068
968
|
const primary = this._theme.primaryColor;
|
|
1069
969
|
const senderPrefix = channel.last_message_sender === "user" ? "You: " : "";
|
|
1070
|
-
const animationDelay = Math.min(index * 0.05, 0.3); // Max 300ms total delay
|
|
1071
970
|
return `
|
|
1072
971
|
<div class="vtilt-channel-item" data-channel-id="${channel.id}" style="
|
|
1073
972
|
padding: 14px 16px;
|
|
@@ -1079,7 +978,6 @@ class LazyLoadedChat {
|
|
|
1079
978
|
gap: 12px;
|
|
1080
979
|
background: white;
|
|
1081
980
|
border-bottom: 1px solid #EEEEEE;
|
|
1082
|
-
animation: vtilt-item-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) ${animationDelay}s both;
|
|
1083
981
|
">
|
|
1084
982
|
<div style="
|
|
1085
983
|
width: 48px;
|
|
@@ -1113,15 +1011,17 @@ class LazyLoadedChat {
|
|
|
1113
1011
|
flex: 1;
|
|
1114
1012
|
min-width: 0;
|
|
1115
1013
|
line-height: 1.4;
|
|
1116
|
-
">${senderPrefix}${this._escapeHTML(preview)}${channel.status === "closed" ?
|
|
1117
|
-
${hasUnread
|
|
1014
|
+
">${senderPrefix}${this._escapeHTML(preview)}${channel.status === "closed" ? " · Closed" : ""}</div>
|
|
1015
|
+
${hasUnread
|
|
1016
|
+
? `<div style="
|
|
1118
1017
|
min-width: 10px;
|
|
1119
1018
|
width: 10px;
|
|
1120
1019
|
height: 10px;
|
|
1121
1020
|
background: ${primary};
|
|
1122
1021
|
border-radius: 50%;
|
|
1123
1022
|
flex-shrink: 0;
|
|
1124
|
-
"></div>`
|
|
1023
|
+
"></div>`
|
|
1024
|
+
: ""}
|
|
1125
1025
|
</div>
|
|
1126
1026
|
</div>
|
|
1127
1027
|
</div>
|
|
@@ -1224,47 +1124,28 @@ class LazyLoadedChat {
|
|
|
1224
1124
|
`;
|
|
1225
1125
|
}
|
|
1226
1126
|
_attachChannelListListeners() {
|
|
1227
|
-
var _a, _b;
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
newChannelBtn === null || newChannelBtn === void 0 ? void 0 : newChannelBtn.addEventListener("click", () => this.createChannel());
|
|
1231
|
-
// Channel items
|
|
1232
|
-
const channelItems = (_b = this._widget) === null || _b === void 0 ? void 0 : _b.querySelectorAll(".vtilt-channel-item");
|
|
1233
|
-
const isTouchDevice = globals_1.window && ("ontouchstart" in globals_1.window || navigator.maxTouchPoints > 0);
|
|
1234
|
-
channelItems === null || channelItems === void 0 ? void 0 : channelItems.forEach((item) => {
|
|
1127
|
+
var _a, _b, _c, _d;
|
|
1128
|
+
(_b = (_a = this._widget) === null || _a === void 0 ? void 0 : _a.querySelector(".vtilt-chat-new-channel")) === null || _b === void 0 ? void 0 : _b.addEventListener("click", () => this.createChannel());
|
|
1129
|
+
(_d = (_c = this._widget) === null || _c === void 0 ? void 0 : _c.querySelectorAll(".vtilt-channel-item")) === null || _d === void 0 ? void 0 : _d.forEach((item) => {
|
|
1235
1130
|
item.addEventListener("click", () => {
|
|
1236
1131
|
const channelId = item.getAttribute("data-channel-id");
|
|
1237
|
-
if (channelId)
|
|
1132
|
+
if (channelId) {
|
|
1238
1133
|
this.selectChannel(channelId);
|
|
1134
|
+
}
|
|
1239
1135
|
});
|
|
1240
|
-
// Hover effect (only on non-touch devices)
|
|
1241
|
-
if (!isTouchDevice) {
|
|
1242
|
-
item.addEventListener("mouseenter", () => {
|
|
1243
|
-
item.style.background = "#F5F5F5";
|
|
1244
|
-
});
|
|
1245
|
-
item.addEventListener("mouseleave", () => {
|
|
1246
|
-
item.style.background = "white";
|
|
1247
|
-
});
|
|
1248
|
-
}
|
|
1249
1136
|
});
|
|
1250
1137
|
}
|
|
1251
1138
|
_attachConversationListeners() {
|
|
1252
|
-
var _a, _b;
|
|
1253
|
-
|
|
1254
|
-
const
|
|
1255
|
-
sendBtn === null || sendBtn === void 0 ? void 0 : sendBtn.addEventListener("click", () => this._handleSend());
|
|
1256
|
-
// Input enter key and typing indicator
|
|
1257
|
-
const input = (_b = this._widget) === null || _b === void 0 ? void 0 : _b.querySelector(".vtilt-chat-input");
|
|
1139
|
+
var _a, _b, _c;
|
|
1140
|
+
(_b = (_a = this._widget) === null || _a === void 0 ? void 0 : _a.querySelector(".vtilt-chat-send")) === null || _b === void 0 ? void 0 : _b.addEventListener("click", () => this._handleSend());
|
|
1141
|
+
const input = (_c = this._widget) === null || _c === void 0 ? void 0 : _c.querySelector(".vtilt-chat-input");
|
|
1258
1142
|
input === null || input === void 0 ? void 0 : input.addEventListener("keypress", (e) => {
|
|
1259
1143
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
1260
1144
|
e.preventDefault();
|
|
1261
1145
|
this._handleSend();
|
|
1262
1146
|
}
|
|
1263
1147
|
});
|
|
1264
|
-
|
|
1265
|
-
input === null || input === void 0 ? void 0 : input.addEventListener("input", () => {
|
|
1266
|
-
this._handleUserTyping();
|
|
1267
|
-
});
|
|
1148
|
+
input === null || input === void 0 ? void 0 : input.addEventListener("input", () => this._handleUserTyping());
|
|
1268
1149
|
}
|
|
1269
1150
|
_formatRelativeTime(isoString) {
|
|
1270
1151
|
const date = new Date(isoString);
|
|
@@ -1273,52 +1154,43 @@ class LazyLoadedChat {
|
|
|
1273
1154
|
const diffMins = Math.floor(diffMs / 60000);
|
|
1274
1155
|
const diffHours = Math.floor(diffMs / 3600000);
|
|
1275
1156
|
const diffDays = Math.floor(diffMs / 86400000);
|
|
1276
|
-
if (diffMins < 1)
|
|
1157
|
+
if (diffMins < 1) {
|
|
1277
1158
|
return "Just now";
|
|
1278
|
-
|
|
1159
|
+
}
|
|
1160
|
+
if (diffMins < 60) {
|
|
1279
1161
|
return `${diffMins}m ago`;
|
|
1280
|
-
|
|
1162
|
+
}
|
|
1163
|
+
if (diffHours < 24) {
|
|
1281
1164
|
return `${diffHours}h ago`;
|
|
1282
|
-
|
|
1165
|
+
}
|
|
1166
|
+
if (diffDays < 7) {
|
|
1283
1167
|
return `${diffDays}d ago`;
|
|
1168
|
+
}
|
|
1284
1169
|
return date.toLocaleDateString();
|
|
1285
1170
|
}
|
|
1286
1171
|
_renderMessages() {
|
|
1287
1172
|
var _a;
|
|
1288
|
-
const
|
|
1289
|
-
if (!
|
|
1173
|
+
const container = (_a = this._widget) === null || _a === void 0 ? void 0 : _a.querySelector(".vtilt-chat-messages");
|
|
1174
|
+
if (!container) {
|
|
1290
1175
|
return;
|
|
1176
|
+
}
|
|
1291
1177
|
const primary = this._theme.primaryColor;
|
|
1292
|
-
// Find first unread agent message index
|
|
1293
1178
|
const firstUnreadIndex = this._state.messages.findIndex((m) => (m.sender_type === "agent" || m.sender_type === "ai") &&
|
|
1294
1179
|
!this._isMessageReadByUser(m.created_at));
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
html += `
|
|
1302
|
-
<div style="display: flex; align-items: center; gap: 12px; margin: 12px 0;">
|
|
1303
|
-
<div style="flex: 1; height: 1px; background: #DDDDDD;"></div>
|
|
1304
|
-
<span style="font-size: 12px; font-weight: 600; color: ${primary};">New</span>
|
|
1305
|
-
<div style="flex: 1; height: 1px; background: #DDDDDD;"></div>
|
|
1306
|
-
</div>
|
|
1307
|
-
`;
|
|
1308
|
-
}
|
|
1309
|
-
html += this._getMessageHTML(msg);
|
|
1310
|
-
return html;
|
|
1180
|
+
const html = this._state.messages
|
|
1181
|
+
.map((msg, i) => {
|
|
1182
|
+
const divider = i === firstUnreadIndex && firstUnreadIndex > 0
|
|
1183
|
+
? `<div style="display:flex;align-items:center;gap:12px;margin:12px 0"><div style="flex:1;height:1px;background:#DDD"></div><span style="font-size:12px;font-weight:600;color:${primary}">New</span><div style="flex:1;height:1px;background:#DDD"></div></div>`
|
|
1184
|
+
: "";
|
|
1185
|
+
return divider + this._getMessageHTML(msg);
|
|
1311
1186
|
})
|
|
1312
1187
|
.join("");
|
|
1313
|
-
|
|
1314
|
-
|
|
1188
|
+
container.innerHTML = html;
|
|
1189
|
+
container.scrollTop = container.scrollHeight;
|
|
1315
1190
|
}
|
|
1316
1191
|
// ============================================================================
|
|
1317
1192
|
// Private - Styles & HTML
|
|
1318
1193
|
// ============================================================================
|
|
1319
|
-
_isMobile() {
|
|
1320
|
-
return globals_1.window ? globals_1.window.innerWidth < 480 : false;
|
|
1321
|
-
}
|
|
1322
1194
|
_getContainerStyles() {
|
|
1323
1195
|
const isRight = (this._config.position || DEFAULT_POSITION) === "bottom-right";
|
|
1324
1196
|
return `
|
|
@@ -1544,7 +1416,9 @@ class LazyLoadedChat {
|
|
|
1544
1416
|
</div>
|
|
1545
1417
|
`;
|
|
1546
1418
|
}
|
|
1547
|
-
const senderLabel = isAi
|
|
1419
|
+
const senderLabel = isAi
|
|
1420
|
+
? "AI Assistant"
|
|
1421
|
+
: message.sender_name || "Support";
|
|
1548
1422
|
return `
|
|
1549
1423
|
<div class="vtilt-msg" style="
|
|
1550
1424
|
display: flex;
|
|
@@ -1584,12 +1458,10 @@ class LazyLoadedChat {
|
|
|
1584
1458
|
</div>
|
|
1585
1459
|
`;
|
|
1586
1460
|
}
|
|
1587
|
-
/**
|
|
1588
|
-
* Check if a message has been read by the agent using cursor comparison
|
|
1589
|
-
*/
|
|
1590
1461
|
_isMessageReadByAgent(messageCreatedAt) {
|
|
1591
|
-
if (!this._state.agentLastReadAt)
|
|
1462
|
+
if (!this._state.agentLastReadAt) {
|
|
1592
1463
|
return false;
|
|
1464
|
+
}
|
|
1593
1465
|
return new Date(messageCreatedAt) <= new Date(this._state.agentLastReadAt);
|
|
1594
1466
|
}
|
|
1595
1467
|
// ============================================================================
|
|
@@ -1628,8 +1500,9 @@ class LazyLoadedChat {
|
|
|
1628
1500
|
return 0;
|
|
1629
1501
|
}
|
|
1630
1502
|
_escapeHTML(text) {
|
|
1631
|
-
if (!globals_1.document)
|
|
1503
|
+
if (!globals_1.document) {
|
|
1632
1504
|
return text;
|
|
1505
|
+
}
|
|
1633
1506
|
const div = globals_1.document.createElement("div");
|
|
1634
1507
|
div.textContent = text;
|
|
1635
1508
|
return div.innerHTML;
|