@nyaruka/temba-components 0.91.5 → 0.92.0

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.
Files changed (92) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/demo/index.html +1 -1
  3. package/dist/temba-components.js +759 -1186
  4. package/dist/temba-components.js.map +1 -1
  5. package/out-tsc/src/chat/Chat.js +714 -0
  6. package/out-tsc/src/chat/Chat.js.map +1 -0
  7. package/out-tsc/src/completion/helpers.js +1 -29
  8. package/out-tsc/src/completion/helpers.js.map +1 -1
  9. package/out-tsc/src/compose/Compose.js +6 -2
  10. package/out-tsc/src/compose/Compose.js.map +1 -1
  11. package/out-tsc/src/contacts/ContactChat.js +518 -54
  12. package/out-tsc/src/contacts/ContactChat.js.map +1 -1
  13. package/out-tsc/src/contacts/events.js +1 -998
  14. package/out-tsc/src/contacts/events.js.map +1 -1
  15. package/out-tsc/src/interfaces.js +1 -0
  16. package/out-tsc/src/interfaces.js.map +1 -1
  17. package/out-tsc/src/lightbox/Lightbox.js +4 -0
  18. package/out-tsc/src/lightbox/Lightbox.js.map +1 -1
  19. package/out-tsc/src/list/TembaMenu.js +0 -1
  20. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  21. package/out-tsc/src/markdown.js +33 -0
  22. package/out-tsc/src/markdown.js.map +1 -0
  23. package/out-tsc/src/mediapicker/MediaPicker.js +6 -0
  24. package/out-tsc/src/mediapicker/MediaPicker.js.map +1 -1
  25. package/out-tsc/src/select/Select.js +6 -1
  26. package/out-tsc/src/select/Select.js.map +1 -1
  27. package/out-tsc/src/templates/TemplateEditor.js +18 -5
  28. package/out-tsc/src/templates/TemplateEditor.js.map +1 -1
  29. package/out-tsc/src/textinput/TextInput.js +1 -1
  30. package/out-tsc/src/textinput/TextInput.js.map +1 -1
  31. package/out-tsc/src/thumbnail/Thumbnail.js +128 -81
  32. package/out-tsc/src/thumbnail/Thumbnail.js.map +1 -1
  33. package/out-tsc/src/utils/index.js +9 -11
  34. package/out-tsc/src/utils/index.js.map +1 -1
  35. package/out-tsc/src/webchat/WebChat.js +109 -358
  36. package/out-tsc/src/webchat/WebChat.js.map +1 -1
  37. package/out-tsc/src/webchat/index.js +17 -0
  38. package/out-tsc/src/webchat/index.js.map +1 -1
  39. package/out-tsc/temba-modules.js +2 -2
  40. package/out-tsc/temba-modules.js.map +1 -1
  41. package/out-tsc/temba-webchat.js +2 -0
  42. package/out-tsc/temba-webchat.js.map +1 -1
  43. package/out-tsc/test/temba-contact-chat.test.js +1 -0
  44. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  45. package/out-tsc/test/temba-lightbox.test.js +4 -4
  46. package/out-tsc/test/temba-lightbox.test.js.map +1 -1
  47. package/package.json +1 -1
  48. package/screenshots/truth/contacts/compose-attachments-no-text-failure.png +0 -0
  49. package/screenshots/truth/contacts/compose-attachments-no-text-success.png +0 -0
  50. package/screenshots/truth/contacts/compose-text-and-attachments-failure-attachments.png +0 -0
  51. package/screenshots/truth/contacts/compose-text-and-attachments-failure-generic.png +0 -0
  52. package/screenshots/truth/contacts/compose-text-and-attachments-failure-text-and-attachments.png +0 -0
  53. package/screenshots/truth/contacts/compose-text-and-attachments-failure-text.png +0 -0
  54. package/screenshots/truth/contacts/compose-text-and-attachments-success.png +0 -0
  55. package/screenshots/truth/contacts/compose-text-no-attachments-failure.png +0 -0
  56. package/screenshots/truth/contacts/compose-text-no-attachments-success.png +0 -0
  57. package/screenshots/truth/contacts/contact-active-default.png +0 -0
  58. package/screenshots/truth/contacts/contact-active-show-chatbox.png +0 -0
  59. package/screenshots/truth/contacts/contact-archived-hide-chatbox.png +0 -0
  60. package/screenshots/truth/contacts/contact-blocked-hide-chatbox.png +0 -0
  61. package/screenshots/truth/contacts/contact-stopped-hide-chatbox.png +0 -0
  62. package/screenshots/truth/lightbox/img-zoomed.png +0 -0
  63. package/screenshots/truth/lightbox/img.png +0 -0
  64. package/src/chat/Chat.ts +791 -0
  65. package/src/completion/helpers.ts +2 -40
  66. package/src/compose/Compose.ts +6 -2
  67. package/src/contacts/ContactChat.ts +609 -59
  68. package/src/contacts/events.ts +1 -1068
  69. package/src/interfaces.ts +1 -0
  70. package/src/lightbox/Lightbox.ts +5 -0
  71. package/src/list/TembaMenu.ts +0 -1
  72. package/src/markdown.ts +41 -0
  73. package/src/mediapicker/MediaPicker.ts +9 -1
  74. package/src/select/Select.ts +5 -1
  75. package/src/templates/TemplateEditor.ts +21 -5
  76. package/src/textinput/TextInput.ts +1 -1
  77. package/src/thumbnail/Thumbnail.ts +130 -81
  78. package/src/utils/index.ts +12 -13
  79. package/src/webchat/WebChat.ts +196 -413
  80. package/src/webchat/index.ts +23 -1
  81. package/static/css/temba-components.css +2 -0
  82. package/temba-modules.ts +2 -2
  83. package/temba-webchat.ts +2 -0
  84. package/test/temba-contact-chat.test.ts +1 -0
  85. package/test/temba-lightbox.test.ts +4 -4
  86. package/test-assets/contacts/history.json +1 -56
  87. package/out-tsc/src/contacts/ContactHistory.js +0 -691
  88. package/out-tsc/src/contacts/ContactHistory.js.map +0 -1
  89. package/out-tsc/test/temba-contact-history.test.js +0 -69
  90. package/out-tsc/test/temba-contact-history.test.js.map +0 -1
  91. package/src/contacts/ContactHistory.ts +0 -875
  92. package/test/temba-contact-history.test.ts +0 -107
@@ -4,30 +4,24 @@ import { LitElement, html, css } from 'lit';
4
4
  import { property } from 'lit/decorators.js';
5
5
  import { getCookie, setCookie } from '../utils';
6
6
  import { DEFAULT_AVATAR } from './assets';
7
+ import { MessageType } from '../chat/Chat';
7
8
  var ChatStatus;
8
9
  (function (ChatStatus) {
9
10
  ChatStatus["DISCONNECTED"] = "disconnected";
10
11
  ChatStatus["CONNECTING"] = "connecting";
11
12
  ChatStatus["CONNECTED"] = "connected";
12
13
  })(ChatStatus || (ChatStatus = {}));
13
- // how long of a window to show time between batches
14
- const BATCH_TIME_WINDOW = 30 * 60 * 1000;
15
- const SCROLL_FETCH_BUFFER = 0.05;
16
- const MIN_FETCH_TIME = 250;
17
- const TIME_FORMAT = { hour: 'numeric', minute: '2-digit' };
18
- const DAY_FORMAT = {
19
- weekday: undefined,
20
- year: 'numeric',
21
- month: 'short',
22
- day: 'numeric'
23
- };
24
- const VERBOSE_FORMAT = {
25
- weekday: undefined,
26
- year: undefined,
27
- month: 'short',
28
- day: 'numeric',
29
- hour: 'numeric',
30
- minute: '2-digit'
14
+ const sockToChat = function (msg) {
15
+ const type = msg.msg_in ? MessageType.MsgIn : MessageType.MsgOut;
16
+ const msgContent = msg.msg_in || msg.msg_out;
17
+ return {
18
+ id: msgContent.id,
19
+ type,
20
+ text: msgContent.text,
21
+ date: new Date(msgContent.time),
22
+ user: msgContent.user,
23
+ attachments: msgContent.attachments
24
+ };
31
25
  };
32
26
  export class WebChat extends LitElement {
33
27
  static get styles() {
@@ -108,6 +102,11 @@ export class WebChat extends LitElement {
108
102
  background: #fff;
109
103
  }
110
104
 
105
+ .border {
106
+ border-top: 1px solid #e9e9e9;
107
+ margin: 0 1em;
108
+ }
109
+
111
110
  .avatar {
112
111
  margin-top: 0.6em;
113
112
  margin-right: 0.6em;
@@ -180,6 +179,7 @@ export class WebChat extends LitElement {
180
179
  }
181
180
 
182
181
  .chat {
182
+ height: 40rem;
183
183
  width: 28rem;
184
184
  border-radius: var(--curvature);
185
185
  overflow: hidden;
@@ -192,6 +192,9 @@ export class WebChat extends LitElement {
192
192
  transform: scale(0.9);
193
193
  pointer-events: none;
194
194
  opacity: 0;
195
+ display: flex;
196
+ flex-direction: column;
197
+ background: #fff;
195
198
  }
196
199
 
197
200
  .chat.open {
@@ -201,61 +204,6 @@ export class WebChat extends LitElement {
201
204
  pointer-events: initial;
202
205
  }
203
206
 
204
- .messages {
205
- background: #fff;
206
- }
207
-
208
- .scroll {
209
- height: 40rem;
210
- max-height: 60vh;
211
- overflow: auto;
212
- -webkit-overflow-scrolling: touch;
213
- overflow-scrolling: touch;
214
- padding: 1em 1em 0 1em;
215
- display: flex;
216
- flex-direction: column-reverse;
217
- }
218
-
219
- .messages:before {
220
- content: '';
221
- background: /* Shadow TOP */ radial-gradient(
222
- farthest-side at 50% 0,
223
- rgba(0, 0, 0, 0.2),
224
- rgba(0, 0, 0, 0)
225
- )
226
- center top;
227
- height: 10px;
228
- display: block;
229
- position: absolute;
230
- width: 28rem;
231
- transition: opacity var(--toggle-speed) ease-out;
232
- }
233
-
234
- .messages:after {
235
- content: '';
236
- background: /* Shadow BOTTOM */ radial-gradient(
237
- farthest-side at 50% 100%,
238
- rgba(0, 0, 0, 0.2),
239
- rgba(0, 0, 0, 0)
240
- )
241
- center bottom;
242
- height: 10px;
243
- display: block;
244
- position: absolute;
245
- margin-top: -10px;
246
- width: 28rem;
247
- margin-right: 5em;
248
- transition: opacity var(--toggle-speed) ease-out;
249
- }
250
-
251
- .scroll-at-top .messages:before {
252
- opacity: 0;
253
- }
254
-
255
- .scroll-at-bottom .messages:after {
256
- opacity: 0;
257
- }
258
-
259
207
  .input {
260
208
  border: none;
261
209
  flex-grow: 1;
@@ -337,20 +285,22 @@ export class WebChat extends LitElement {
337
285
  this.status = ChatStatus.DISCONNECTED;
338
286
  // is the chat widget open
339
287
  this.open = false;
340
- this.fetching = false;
341
288
  this.hasPendingText = false;
342
- this.hideTopScroll = true;
343
- this.hideBottomScroll = true;
344
289
  this.blockHistoryFetching = false;
345
- this.msgMap = new Map();
346
290
  this.newMessageCount = 0;
347
291
  }
292
+ firstUpdated(changed) {
293
+ super.firstUpdated(changed);
294
+ this.chat = this.shadowRoot.querySelector('temba-chat');
295
+ const lightbox = document.createElement('temba-lightbox');
296
+ document.querySelector('body').appendChild(lightbox);
297
+ }
348
298
  handleReconnect() {
349
299
  this.openSocket();
350
300
  }
351
- sendSockMessage(message) {
352
- console.log('MO', message);
353
- this.sock.send(JSON.stringify(message));
301
+ sendSockMessage(cmd) {
302
+ console.log('out', cmd);
303
+ this.sock.send(JSON.stringify(cmd));
354
304
  }
355
305
  openSocket() {
356
306
  if (this.status !== ChatStatus.DISCONNECTED) {
@@ -358,155 +308,89 @@ export class WebChat extends LitElement {
358
308
  }
359
309
  this.status = ChatStatus.CONNECTING;
360
310
  const webChat = this;
361
- let url = `wss://localhost.textit.com/connect/${this.channel}/`;
311
+ let url = `wss://localhost.textit.com/wc/connect/${this.channel}/`;
362
312
  if (this.urn) {
363
313
  url = `${url}?chat_id=${this.urn}`;
364
314
  }
365
315
  const sock = new WebSocket(url);
366
316
  this.sock = sock;
367
- this.sock.onclose = function (event) {
368
- console.log('Socket closed', event);
317
+ this.sock.onclose = function () {
369
318
  webChat.status = ChatStatus.DISCONNECTED;
370
319
  };
371
- this.sock.onopen = function (event) {
372
- console.log('Socket opened', event);
320
+ this.sock.onopen = function () {
321
+ webChat.beforeTime = new Date().toISOString();
373
322
  webChat.status = ChatStatus.CONNECTED;
374
323
  webChat.urn = getCookie('temba-chat-urn');
375
- const startChat = { type: 'start_chat' };
324
+ const cmd = { type: 'start_chat' };
376
325
  if (webChat.urn) {
377
- startChat['chat_id'] = webChat.urn;
326
+ cmd.chat_id = webChat.urn;
378
327
  }
379
- webChat.sendSockMessage(startChat);
328
+ webChat.sendSockMessage(cmd);
380
329
  };
381
330
  this.sock.onmessage = function (event) {
382
331
  webChat.status = ChatStatus.CONNECTED;
383
332
  const msg = JSON.parse(event.data);
384
- console.log('MT', msg);
333
+ console.log('in', msg);
385
334
  if (msg.type === 'chat_started') {
386
- if (webChat.urn !== msg.chat_id) {
335
+ const response = msg;
336
+ if (webChat.urn !== response.chat_id) {
387
337
  webChat.messageGroups = [];
388
338
  }
389
- webChat.urn = msg.chat_id;
390
- setCookie('temba-chat-urn', msg.chat_id);
339
+ webChat.urn = response.chat_id;
340
+ setCookie('temba-chat-urn', response.chat_id);
391
341
  webChat.requestUpdate('messageGroups');
392
342
  }
393
343
  else if (msg.type === 'chat_resumed') {
394
- webChat.oldestMessageDate = new Date(msg.time);
395
- webChat.urn = msg.chat_id;
344
+ const response = msg;
345
+ webChat.urn = response.chat_id;
396
346
  webChat.fetchPreviousMessages();
397
347
  }
398
- else if (msg.type === 'msg_out') {
399
- webChat.addMessage(msg);
400
- webChat.insertGroups(webChat.groupMessages([msg.msg_id]), true);
348
+ else if (msg.type === 'chat_out') {
349
+ const response = msg;
350
+ webChat.chat.addMessages([sockToChat(response)], null, true);
351
+ // ack receipt
352
+ const ack = { type: 'ack_chat', msg_id: response.msg_out.id };
353
+ webChat.sendSockMessage(ack);
401
354
  }
402
355
  else if (msg.type === 'history') {
403
356
  webChat.handleHistoryResponse(msg);
404
357
  }
405
358
  };
406
- }
407
- isSameGroup(msg1, msg2) {
408
- var _a, _b;
409
- if (msg1 && msg2) {
410
- return (msg1.origin === msg2.origin &&
411
- ((_a = msg1.user) === null || _a === void 0 ? void 0 : _a.name) === ((_b = msg2.user) === null || _b === void 0 ? void 0 : _b.name) &&
412
- Math.abs(msg1.timeAsDate.getTime() - msg2.timeAsDate.getTime()) <
413
- BATCH_TIME_WINDOW);
414
- }
415
- return false;
416
- }
417
- insertGroups(newGroups, append = false) {
418
- newGroups.reverse();
419
- for (const newGroup of newGroups) {
420
- // see if our new group belongs to the most recent group
421
- const group = this.messageGroups[append ? 0 : this.messageGroups.length - 1];
422
- if (group) {
423
- const lastMsgId = group[group.length - 1];
424
- const lastMsg = this.msgMap.get(lastMsgId);
425
- const newMsg = this.msgMap.get(newGroup[0]);
426
- // if our message belongs to the previous group, in we go
427
- if (this.isSameGroup(lastMsg, newMsg)) {
428
- group.push(...newGroup);
429
- }
430
- else {
431
- // otherwise, just add our entire group as a new one
432
- if (append) {
433
- this.messageGroups.splice(0, 0, newGroup);
434
- }
435
- else {
436
- this.messageGroups.push(newGroup);
437
- }
438
- }
439
- }
440
- else {
441
- if (append) {
442
- this.messageGroups.splice(0, 0, newGroup);
443
- }
444
- else {
445
- this.messageGroups.push(newGroup);
446
- }
447
- }
448
- }
449
- this.requestUpdate('messageGroups');
450
- }
451
- groupMessages(msgIds) {
452
- // group our messages by origin and user
453
- const groups = [];
454
- let lastGroup = [];
455
- let lastMsg = null;
456
- for (const msgId of msgIds) {
457
- const msg = this.msgMap.get(msgId);
458
- if (!this.isSameGroup(msg, lastMsg)) {
459
- lastGroup = [];
460
- groups.push(lastGroup);
461
- }
462
- lastGroup.push(msgId);
463
- lastMsg = msg;
464
- }
465
- return groups;
359
+ this.sock.onerror = function () {
360
+ webChat.status = ChatStatus.DISCONNECTED;
361
+ };
466
362
  }
467
363
  fetchPreviousMessages() {
468
364
  if (!this.blockHistoryFetching) {
365
+ this.fetchRequested = new Date();
469
366
  this.blockHistoryFetching = true;
470
- this.fetching = true;
471
- const getHistoryMsg = { type: 'get_history' };
472
- if (this.oldestMessageDate) {
473
- getHistoryMsg['before'] = this.oldestMessageDate.toISOString();
474
- }
367
+ this.chat.fetching = true;
368
+ const cmd = {
369
+ type: 'get_history',
370
+ before: this.beforeTime
371
+ };
475
372
  this.fetchRequested = new Date();
476
- this.sendSockMessage(getHistoryMsg);
373
+ this.sendSockMessage(cmd);
477
374
  }
478
375
  }
479
- handleHistoryResponse(msg) {
480
- const elapsed = new Date().getTime() - this.fetchRequested.getTime();
481
- window.setTimeout(() => {
482
- this.fetching = false;
483
- // block of historical messages
484
- const msgs = msg.history.reverse();
485
- // first add messages to the map
486
- const newMessages = [];
487
- for (const m of msgs) {
488
- if (this.addMessage(m)) {
489
- newMessages.push(m.msg_id);
490
- }
376
+ handleHistoryResponse(response) {
377
+ const messages = response.history.reverse();
378
+ if (messages.length > 0) {
379
+ const oldestMessage = messages[0];
380
+ if (oldestMessage['msg_in']) {
381
+ const msgIn = oldestMessage.msg_in;
382
+ this.beforeTime = msgIn.time;
491
383
  }
492
- if (newMessages.length === 0) {
493
- return;
384
+ else if (oldestMessage['msg_out']) {
385
+ const msgOut = oldestMessage.msg_out;
386
+ this.beforeTime = msgOut.time;
494
387
  }
495
- this.insertGroups(this.groupMessages(newMessages));
496
- const ele = this.shadowRoot.querySelector('.scroll');
497
- const prevTop = ele.scrollTop;
498
- window.setTimeout(() => {
499
- ele.scrollTop = prevTop;
500
- this.blockHistoryFetching = false;
501
- }, 100);
502
- },
503
- // if it's the first load don't wait, otherwise wait a minimum amount of time
504
- this.messageGroups.length === 0
505
- ? 0
506
- : Math.max(0, MIN_FETCH_TIME - elapsed));
388
+ }
389
+ // convert messages to chat messages
390
+ this.chat.addMessages(messages.map(sockToChat), this.fetchRequested);
507
391
  }
508
- firstUpdated(changed) {
509
- super.firstUpdated(changed);
392
+ fetchComplete() {
393
+ this.blockHistoryFetching = false;
510
394
  }
511
395
  focusInput() {
512
396
  const input = this.shadowRoot.querySelector('.input');
@@ -517,11 +401,6 @@ export class WebChat extends LitElement {
517
401
  updated(changed) {
518
402
  super.updated(changed);
519
403
  if (this.open && changed.has('open') && changed.get('open') !== undefined) {
520
- const scroll = this.shadowRoot.querySelector('.scroll');
521
- const hasScroll = scroll.scrollHeight > scroll.clientHeight;
522
- this.hideBottomScroll = true;
523
- this.hideTopScroll = !hasScroll;
524
- this.scrollToBottom();
525
404
  if (this.status === ChatStatus.DISCONNECTED) {
526
405
  this.openSocket();
527
406
  }
@@ -532,22 +411,6 @@ export class WebChat extends LitElement {
532
411
  }
533
412
  }
534
413
  }
535
- addMessage(msg) {
536
- var _a;
537
- if (msg.time && !msg.timeAsDate) {
538
- msg.timeAsDate = new Date(msg.time);
539
- }
540
- if (!this.oldestMessageDate ||
541
- msg.timeAsDate.getTime() < this.oldestMessageDate.getTime()) {
542
- this.oldestMessageDate = msg.timeAsDate;
543
- }
544
- const isNew = !this.msgMap.has(msg.msg_id);
545
- this.msgMap.set(msg.msg_id, msg);
546
- if ((_a = msg.user) === null || _a === void 0 ? void 0 : _a.avatar) {
547
- this.activeUserAvatar = msg.user.avatar;
548
- }
549
- return isNew;
550
- }
551
414
  openChat() {
552
415
  this.open = true;
553
416
  }
@@ -563,109 +426,17 @@ export class WebChat extends LitElement {
563
426
  const text = input.value;
564
427
  input.value = '';
565
428
  const msg = {
566
- msg_id: `pending-${this.newMessageCount++}`,
429
+ // msg_id: `pending-${this.newMessageCount++}`,
567
430
  type: 'send_msg',
568
- text: text,
569
- time: new Date().toISOString()
431
+ text: text
432
+ // time: new Date().toISOString()
570
433
  };
571
- this.addMessage(msg);
572
- this.insertGroups(this.groupMessages([msg.msg_id]), true);
573
434
  this.sendSockMessage(msg);
435
+ const date = new Date();
436
+ this.chat.addMessages([{ type: MessageType.MsgIn, text, date }], date, true);
574
437
  this.hasPendingText = input.value.length > 0;
575
438
  }
576
439
  }
577
- scrollToBottom() {
578
- const scroll = this.shadowRoot.querySelector('.scroll');
579
- if (scroll) {
580
- scroll.scrollTop = scroll.scrollHeight;
581
- this.hideBottomScroll = true;
582
- }
583
- }
584
- renderMessageGroup(msgIds, idx, groups) {
585
- var _a, _b;
586
- const today = new Date();
587
- let prevMsg;
588
- if (idx > 0) {
589
- const lastGroup = groups[idx - 1];
590
- if (lastGroup && lastGroup.length > 0) {
591
- prevMsg = this.msgMap.get(lastGroup[0]);
592
- }
593
- }
594
- const currentMsg = this.msgMap.get(msgIds[msgIds.length - 1]);
595
- let timeDisplay = null;
596
- if (prevMsg &&
597
- !this.isSameGroup(prevMsg, currentMsg) &&
598
- prevMsg.timeAsDate.getTime() - currentMsg.timeAsDate.getTime() >
599
- BATCH_TIME_WINDOW) {
600
- const showDay = !prevMsg ||
601
- prevMsg.timeAsDate.getDate() !== currentMsg.timeAsDate.getDate();
602
- if (showDay) {
603
- timeDisplay = html `<div class="time">
604
- ${prevMsg.timeAsDate.toLocaleDateString(undefined, DAY_FORMAT)}
605
- </div>`;
606
- }
607
- else {
608
- if (prevMsg.timeAsDate.getDate() !== today.getDate()) {
609
- timeDisplay = html `<div class="time">
610
- ${prevMsg.timeAsDate.toLocaleTimeString(undefined, VERBOSE_FORMAT)}
611
- </div>`;
612
- }
613
- else {
614
- timeDisplay = html `<div class="time">
615
- ${prevMsg.timeAsDate.toLocaleTimeString(undefined, TIME_FORMAT)}
616
- </div>`;
617
- }
618
- }
619
- }
620
- const blockTime = new Date(this.msgMap.get(msgIds[msgIds.length - 1]).time);
621
- const message = this.msgMap.get(msgIds[0]);
622
- const incoming = !message.origin;
623
- const avatar = (_a = message.user) === null || _a === void 0 ? void 0 : _a.avatar;
624
- const name = (_b = message.user) === null || _b === void 0 ? void 0 : _b.name;
625
- return html ` <div
626
- class="block ${incoming ? 'incoming' : 'outgoing'} ${idx === 0
627
- ? 'first'
628
- : ''}"
629
- title="${blockTime.toLocaleTimeString(undefined, VERBOSE_FORMAT)}"
630
- >
631
- <div class="row">
632
- ${!incoming
633
- ? html `
634
- <div
635
- class="avatar"
636
- style="background: center / contain no-repeat url(${avatar ||
637
- DEFAULT_AVATAR})"
638
- ></div>
639
- `
640
- : null}
641
-
642
- <div class="bubble">
643
- ${!incoming ? html `<div class="name">${name}</div>` : null}
644
- ${msgIds.map((msgId) => html `<div class="message">${this.msgMap.get(msgId).text}</div>
645
- <!--div style="font-size:10px">
646
- ${this.msgMap
647
- .get(msgId)
648
- .timeAsDate.toLocaleDateString(undefined, VERBOSE_FORMAT)}
649
- </div-->`)}
650
- </div>
651
- </div>
652
- ${timeDisplay}
653
- </div>`;
654
- }
655
- handleScroll(event) {
656
- const ele = event.target;
657
- const top = ele.scrollHeight - ele.clientHeight;
658
- const scroll = Math.round(top + ele.scrollTop);
659
- const scrollPct = scroll / top;
660
- this.hideTopScroll = scrollPct <= 0.01;
661
- this.hideBottomScroll = scrollPct >= 0.99;
662
- if (this.blockHistoryFetching) {
663
- return;
664
- }
665
- if (scrollPct < SCROLL_FETCH_BUFFER) {
666
- this.fetchPreviousMessages();
667
- }
668
- }
669
440
  handleClickInputPanel(event) {
670
441
  event.preventDefault();
671
442
  event.stopPropagation();
@@ -677,13 +448,7 @@ export class WebChat extends LitElement {
677
448
  }
678
449
  render() {
679
450
  return html `
680
- <div
681
- class="chat ${this.status} ${this.hideTopScroll
682
- ? 'scroll-at-top'
683
- : ''} ${this.hideBottomScroll ? 'scroll-at-bottom' : ''} ${this.open
684
- ? 'open'
685
- : ''}"
686
- >
451
+ <div class="chat ${this.status} ${this.open ? 'open' : ''}">
687
452
  <div class="header">
688
453
  <slot name="header">${this.urn ? this.urn : 'Chat'}</slot>
689
454
  <temba-icon
@@ -693,17 +458,11 @@ export class WebChat extends LitElement {
693
458
  @click=${this.toggleChat}
694
459
  ></temba-icon>
695
460
  </div>
696
- <div class="messages">
697
- <div class="scroll" @scroll=${this.handleScroll}>
698
- ${this.messageGroups
699
- ? this.messageGroups.map((msgGroup, idx, groups) => html `${this.renderMessageGroup(msgGroup, idx, groups)}`)
700
- : null}
701
461
 
702
- <temba-loading
703
- class="${!this.fetching ? 'hidden' : ''}"
704
- ></temba-loading>
705
- </div>
706
- </div>
462
+ <temba-chat
463
+ @temba-scroll-threshold=${this.fetchPreviousMessages}
464
+ @temba-fetch-complete=${this.fetchComplete}
465
+ ></temba-chat>
707
466
 
708
467
  ${this.status === ChatStatus.DISCONNECTED
709
468
  ? html `<div class="notice">
@@ -721,28 +480,29 @@ export class WebChat extends LitElement {
721
480
  </div>`
722
481
  : null}
723
482
  ${this.status === ChatStatus.CONNECTED
724
- ? html ` <div
725
- class="row input-panel ${this.hasPendingText ? 'pending' : ''}"
726
- @click=${this.handleClickInputPanel}
727
- >
728
- <input
729
- class="input ${this.status === ChatStatus.CONNECTED
483
+ ? html ` <div class="border"></div>
484
+ <div
485
+ class="row input-panel ${this.hasPendingText ? 'pending' : ''}"
486
+ @click=${this.handleClickInputPanel}
487
+ >
488
+ <input
489
+ class="input ${this.status === ChatStatus.CONNECTED
730
490
  ? 'active'
731
491
  : 'inactive'}"
732
- type="text"
733
- placeholder="Message.."
734
- ?disabled=${this.status !== ChatStatus.CONNECTED}
735
- @keydown=${this.handleKeyUp}
736
- />
737
- <temba-icon
738
- tabindex="1"
739
- class="send-icon"
740
- name="send"
741
- size="1"
742
- clickable
743
- @click=${this.sendPendingMessage}
744
- ></temba-icon>
745
- </div>`
492
+ type="text"
493
+ placeholder="Message.."
494
+ ?disabled=${this.status !== ChatStatus.CONNECTED}
495
+ @keydown=${this.handleKeyUp}
496
+ />
497
+ <temba-icon
498
+ tabindex="1"
499
+ class="send-icon"
500
+ name="send"
501
+ size="1"
502
+ clickable
503
+ @click=${this.sendPendingMessage}
504
+ ></temba-icon>
505
+ </div>`
746
506
  : null}
747
507
  </div>
748
508
 
@@ -771,25 +531,16 @@ __decorate([
771
531
  __decorate([
772
532
  property({ type: Boolean })
773
533
  ], WebChat.prototype, "open", void 0);
774
- __decorate([
775
- property({ type: Boolean })
776
- ], WebChat.prototype, "fetching", void 0);
777
534
  __decorate([
778
535
  property({ type: Boolean })
779
536
  ], WebChat.prototype, "hasPendingText", void 0);
780
- __decorate([
781
- property({ type: Boolean, attribute: false })
782
- ], WebChat.prototype, "hideTopScroll", void 0);
783
- __decorate([
784
- property({ type: Boolean, attribute: false })
785
- ], WebChat.prototype, "hideBottomScroll", void 0);
786
- __decorate([
787
- property({ type: Boolean, attribute: false })
788
- ], WebChat.prototype, "blockHistoryFetching", void 0);
789
537
  __decorate([
790
538
  property({ type: String })
791
539
  ], WebChat.prototype, "host", void 0);
792
540
  __decorate([
793
541
  property({ type: String })
794
542
  ], WebChat.prototype, "activeUserAvatar", void 0);
543
+ __decorate([
544
+ property({ type: Boolean, attribute: false })
545
+ ], WebChat.prototype, "blockHistoryFetching", void 0);
795
546
  //# sourceMappingURL=WebChat.js.map