@nyaruka/temba-components 0.91.6 → 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 (83) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/demo/index.html +1 -1
  3. package/dist/temba-components.js +760 -1189
  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/lightbox/Lightbox.js +4 -0
  16. package/out-tsc/src/lightbox/Lightbox.js.map +1 -1
  17. package/out-tsc/src/list/TembaMenu.js +0 -1
  18. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  19. package/out-tsc/src/markdown.js +33 -0
  20. package/out-tsc/src/markdown.js.map +1 -0
  21. package/out-tsc/src/select/Select.js +6 -1
  22. package/out-tsc/src/select/Select.js.map +1 -1
  23. package/out-tsc/src/textinput/TextInput.js +1 -1
  24. package/out-tsc/src/textinput/TextInput.js.map +1 -1
  25. package/out-tsc/src/thumbnail/Thumbnail.js +128 -81
  26. package/out-tsc/src/thumbnail/Thumbnail.js.map +1 -1
  27. package/out-tsc/src/utils/index.js +9 -11
  28. package/out-tsc/src/utils/index.js.map +1 -1
  29. package/out-tsc/src/webchat/WebChat.js +109 -358
  30. package/out-tsc/src/webchat/WebChat.js.map +1 -1
  31. package/out-tsc/src/webchat/index.js +17 -0
  32. package/out-tsc/src/webchat/index.js.map +1 -1
  33. package/out-tsc/temba-modules.js +2 -2
  34. package/out-tsc/temba-modules.js.map +1 -1
  35. package/out-tsc/temba-webchat.js +2 -0
  36. package/out-tsc/temba-webchat.js.map +1 -1
  37. package/out-tsc/test/temba-contact-chat.test.js +1 -0
  38. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  39. package/out-tsc/test/temba-lightbox.test.js +4 -4
  40. package/out-tsc/test/temba-lightbox.test.js.map +1 -1
  41. package/package.json +1 -1
  42. package/screenshots/truth/contacts/compose-attachments-no-text-failure.png +0 -0
  43. package/screenshots/truth/contacts/compose-attachments-no-text-success.png +0 -0
  44. package/screenshots/truth/contacts/compose-text-and-attachments-failure-attachments.png +0 -0
  45. package/screenshots/truth/contacts/compose-text-and-attachments-failure-generic.png +0 -0
  46. package/screenshots/truth/contacts/compose-text-and-attachments-failure-text-and-attachments.png +0 -0
  47. package/screenshots/truth/contacts/compose-text-and-attachments-failure-text.png +0 -0
  48. package/screenshots/truth/contacts/compose-text-and-attachments-success.png +0 -0
  49. package/screenshots/truth/contacts/compose-text-no-attachments-failure.png +0 -0
  50. package/screenshots/truth/contacts/compose-text-no-attachments-success.png +0 -0
  51. package/screenshots/truth/contacts/contact-active-default.png +0 -0
  52. package/screenshots/truth/contacts/contact-active-show-chatbox.png +0 -0
  53. package/screenshots/truth/contacts/contact-archived-hide-chatbox.png +0 -0
  54. package/screenshots/truth/contacts/contact-blocked-hide-chatbox.png +0 -0
  55. package/screenshots/truth/contacts/contact-stopped-hide-chatbox.png +0 -0
  56. package/screenshots/truth/lightbox/img-zoomed.png +0 -0
  57. package/screenshots/truth/lightbox/img.png +0 -0
  58. package/src/chat/Chat.ts +791 -0
  59. package/src/completion/helpers.ts +2 -40
  60. package/src/compose/Compose.ts +6 -2
  61. package/src/contacts/ContactChat.ts +609 -59
  62. package/src/contacts/events.ts +1 -1068
  63. package/src/lightbox/Lightbox.ts +5 -0
  64. package/src/list/TembaMenu.ts +0 -1
  65. package/src/markdown.ts +41 -0
  66. package/src/select/Select.ts +5 -1
  67. package/src/textinput/TextInput.ts +1 -1
  68. package/src/thumbnail/Thumbnail.ts +130 -81
  69. package/src/utils/index.ts +12 -13
  70. package/src/webchat/WebChat.ts +196 -413
  71. package/src/webchat/index.ts +23 -1
  72. package/static/css/temba-components.css +2 -0
  73. package/temba-modules.ts +2 -2
  74. package/temba-webchat.ts +2 -0
  75. package/test/temba-contact-chat.test.ts +1 -0
  76. package/test/temba-lightbox.test.ts +4 -4
  77. package/test-assets/contacts/history.json +1 -56
  78. package/out-tsc/src/contacts/ContactHistory.js +0 -691
  79. package/out-tsc/src/contacts/ContactHistory.js.map +0 -1
  80. package/out-tsc/test/temba-contact-history.test.js +0 -69
  81. package/out-tsc/test/temba-contact-history.test.js.map +0 -1
  82. package/src/contacts/ContactHistory.ts +0 -875
  83. package/test/temba-contact-history.test.ts +0 -107
@@ -0,0 +1,714 @@
1
+ import { __decorate } from "tslib";
2
+ import { html, css } from 'lit';
3
+ import { property } from 'lit/decorators.js';
4
+ import { RapidElement } from '../RapidElement';
5
+ import { CustomEventType } from '../interfaces';
6
+ import { DEFAULT_AVATAR } from '../webchat/assets';
7
+ import { hashCode, renderAvatar } from '../utils';
8
+ import { renderMarkdown } from '../markdown';
9
+ const BATCH_TIME_WINDOW = 60 * 60 * 1000;
10
+ const SCROLL_FETCH_BUFFER = 0.05;
11
+ const MIN_FETCH_TIME = 250;
12
+ export var MessageType;
13
+ (function (MessageType) {
14
+ MessageType["Inline"] = "inline";
15
+ MessageType["Error"] = "error";
16
+ MessageType["Collapse"] = "collapse";
17
+ MessageType["Note"] = "note";
18
+ MessageType["MsgIn"] = "msg_in";
19
+ MessageType["MsgOut"] = "msg_out";
20
+ })(MessageType || (MessageType = {}));
21
+ const TIME_FORMAT = { hour: 'numeric', minute: '2-digit' };
22
+ const VERBOSE_FORMAT = {
23
+ weekday: undefined,
24
+ year: undefined,
25
+ month: 'short',
26
+ day: 'numeric',
27
+ hour: 'numeric',
28
+ minute: '2-digit'
29
+ };
30
+ export class Chat extends RapidElement {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.messageGroups = [];
34
+ this.fetching = false;
35
+ this.hideTopScroll = true;
36
+ this.hideBottomScroll = true;
37
+ this.defaultAvatar = DEFAULT_AVATAR;
38
+ this.agent = false;
39
+ this.msgMap = new Map();
40
+ }
41
+ static get styles() {
42
+ return css `
43
+ :host {
44
+ display: flex;
45
+ flex-direction: column;
46
+ flex-grow: 1;
47
+ }
48
+
49
+ .block {
50
+ margin-bottom: 1em;
51
+ }
52
+
53
+ .block.collapse {
54
+ margin: 0;
55
+ align-items: center;
56
+ display: flex;
57
+ flex-direction: column;
58
+ margin-bottom: 0.5em;
59
+ }
60
+
61
+ .block.collapse .messsage {
62
+ transform: scaleY(0);
63
+ margin: 0;
64
+ padding: 0;
65
+ line-height: 0;
66
+ }
67
+
68
+ .time {
69
+ text-align: center;
70
+ font-size: 0.8em;
71
+ color: #999;
72
+ margin-top: 2em;
73
+ border-top: 1px solid #e9e9e9;
74
+ padding: 1em;
75
+ margin-left: 10%;
76
+ margin-right: 10%;
77
+ }
78
+
79
+ .time.first {
80
+ border-top: none;
81
+ margin-top: 0;
82
+ border-bottom: 1px solid #e9e9e9;
83
+ margin-bottom: 2em;
84
+ }
85
+
86
+ .first .time {
87
+ margin-top: 0;
88
+ border-top: none;
89
+ padding-top: 0;
90
+ }
91
+
92
+ .row {
93
+ display: flex;
94
+ flex-direction: row;
95
+ align-items: flex-start;
96
+ margin-bottom: 0.25em;
97
+ }
98
+
99
+ .input-panel {
100
+ padding: 1em;
101
+ background: #fff;
102
+ }
103
+
104
+ .avatar {
105
+ margin-right: 0.6em;
106
+ margin-left: 0.6em;
107
+ width: 2em;
108
+ align-self: flex-end;
109
+ }
110
+
111
+ .toggle {
112
+ flex-shrink: 0;
113
+ width: 4em;
114
+ height: 4em;
115
+ overflow: hidden;
116
+ border-radius: 100%;
117
+ box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 1em 0.7em,
118
+ rgba(0, 0, 0, 0.2) 0px 1px 2px 0px,
119
+ inset 0 0 0 0.25em rgba(0, 0, 0, 0.1);
120
+ cursor: pointer;
121
+ transition: box-shadow var(--toggle-speed, 200ms) ease-out;
122
+ position: absolute;
123
+ bottom: 1em;
124
+ right: 1em;
125
+ }
126
+
127
+ .toggle:hover {
128
+ box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 1em 0.7em,
129
+ rgba(0, 0, 0, 0.4) 0px 1px 2px 0px,
130
+ inset 0 0 0 0.25em rgba(0, 0, 0, 0.2);
131
+ }
132
+
133
+ .incoming .row {
134
+ flex-direction: row-reverse;
135
+ margin-left: 1em;
136
+ }
137
+
138
+ .bubble {
139
+ padding: 0.75em;
140
+ padding-bottom: 0.25em;
141
+ background: var(--color-chat-in, #f1f1f1);
142
+ border-radius: var(--curvature);
143
+ }
144
+
145
+ .bubble .name {
146
+ font-size: 0.95em;
147
+ font-weight: 400;
148
+ color: rgba(0, 0, 0, 0.4);
149
+ margin-bottom: 0.25em;
150
+ }
151
+
152
+ .outgoing .latest .bubble {
153
+ border-bottom-left-radius: 0;
154
+ }
155
+
156
+ .incoming .bubble {
157
+ background: var(--color-chat-out, #3c92dd);
158
+ color: white;
159
+ }
160
+
161
+ .incoming .latest .bubble {
162
+ border-bottom-right-radius: 0;
163
+ }
164
+
165
+ .incoming .bubble .name {
166
+ color: rgba(255, 255, 255, 0.7);
167
+ }
168
+
169
+ .note .bubble {
170
+ background: #fff47b;
171
+ color: rgba(0, 0, 0, 0.6);
172
+ }
173
+
174
+ .message {
175
+ margin-bottom: 0.5em;
176
+ line-height: 1.2em;
177
+ }
178
+
179
+ .chat {
180
+ width: 28rem;
181
+ border-radius: var(--curvature);
182
+ overflow: hidden;
183
+ box-shadow: rgba(0, 0, 0, 0.1) 0px 3px 7px 0px,
184
+ rgba(0, 0, 0, 0.2) 0px 1px 2px 0px, rgba(0, 0, 0, 0.1) 5em 5em 5em 5em;
185
+ position: absolute;
186
+ bottom: 3em;
187
+ right: 1em;
188
+ transition: all var(--toggle-speed, 200ms) ease-out;
189
+ transform: scale(0.9);
190
+ pointer-events: none;
191
+ opacity: 0;
192
+ }
193
+
194
+ .chat.open {
195
+ bottom: 6em;
196
+ opacity: 1;
197
+ transform: scale(1);
198
+ pointer-events: initial;
199
+ }
200
+
201
+ .messages {
202
+ background: #fff;
203
+ position: relative;
204
+ flex-grow: 1;
205
+ overflow: hidden;
206
+ }
207
+
208
+ .scroll {
209
+ position: absolute;
210
+ top: 0;
211
+ bottom: 0;
212
+ right: 0;
213
+ left: 0;
214
+ overflow: auto;
215
+ -webkit-overflow-scrolling: touch;
216
+ overflow-scrolling: touch;
217
+ padding: 1em 1em 1em 1em;
218
+ display: flex;
219
+ flex-direction: column-reverse;
220
+ }
221
+
222
+ .messages:before {
223
+ content: '';
224
+ background: radial-gradient(
225
+ farthest-side at 50% 0,
226
+ rgba(0, 0, 0, 0.2),
227
+ rgba(0, 0, 0, 0)
228
+ )
229
+ center top;
230
+ height: 10px;
231
+ display: block;
232
+ position: absolute;
233
+ width: 100%;
234
+ transition: opacity var(--toggle-speed, 200ms) ease-out;
235
+ z-index: 1;
236
+ }
237
+
238
+ .messages:after {
239
+ content: '';
240
+ background: radial-gradient(
241
+ farthest-side at 50% 100%,
242
+ rgba(0, 0, 0, 0.2),
243
+ rgba(0, 0, 0, 0)
244
+ )
245
+ center bottom;
246
+ height: 10px;
247
+ display: block;
248
+ position: absolute;
249
+ bottom: 0;
250
+ margin-top: -10px;
251
+ width: 100%;
252
+ margin-right: 5em;
253
+ transition: opacity var(--toggle-speed, 200ms) ease-out;
254
+ z-index: 1;
255
+ }
256
+
257
+ .bubble-wrap {
258
+ position: relative;
259
+ max-width: 70%;
260
+ display: flex;
261
+ flex-direction: column;
262
+ align-items: center;
263
+ margin: -0.5em -2em;
264
+ padding: 0.5em 2em;
265
+ }
266
+
267
+ .scroll-at-top.messages:before {
268
+ opacity: 0;
269
+ }
270
+
271
+ .scroll-at-bottom.messages:after {
272
+ opacity: 0;
273
+ }
274
+
275
+ .input {
276
+ border: none;
277
+ flex-grow: 1;
278
+ color: #333;
279
+ font-size: 1em;
280
+ }
281
+
282
+ .input:focus {
283
+ outline: none;
284
+ }
285
+
286
+ input::placeholder {
287
+ opacity: 0.3;
288
+ }
289
+
290
+ .input.inactive {
291
+ // pointer-events: none;
292
+ // opacity: 0.3;
293
+ }
294
+
295
+ .active {
296
+ }
297
+
298
+ .send-icon {
299
+ color: #eee;
300
+ pointer-events: none;
301
+ transform: rotate(-45deg);
302
+ transition: transform 0.2s ease-out;
303
+ }
304
+
305
+ .pending .send-icon {
306
+ color: var(--color-primary-dark);
307
+ pointer-events: initial;
308
+ transform: rotate(0deg);
309
+ }
310
+
311
+ .notice {
312
+ padding: 1em;
313
+ background: #f8f8f8;
314
+ color: #666;
315
+ text-align: center;
316
+ cursor: pointer;
317
+ }
318
+
319
+ .connecting .notice {
320
+ display: flex;
321
+ justify-content: center;
322
+ }
323
+
324
+ .connecting .notice temba-icon {
325
+ margin-left: 0.5em;
326
+ }
327
+
328
+ .reconnect {
329
+ color: var(--color-primary-dark);
330
+ text-decoration: underline;
331
+ font-size: 0.9em;
332
+ }
333
+
334
+ .input:disabled {
335
+ background: transparent !important;
336
+ }
337
+
338
+ temba-loading {
339
+ justify-content: center;
340
+ margin: 0.5em auto;
341
+ margin-bottom: 2em;
342
+ }
343
+
344
+ temba-loading.hidden {
345
+ display: none;
346
+ }
347
+
348
+ .inline {
349
+ }
350
+
351
+ .event {
352
+ flex-grow: 1;
353
+ align-self: center;
354
+ display: flex;
355
+ flex-direction: column;
356
+ align-items: center;
357
+ }
358
+
359
+ .event p {
360
+ margin: 0;
361
+ padding: 0;
362
+ }
363
+
364
+ .collapse {
365
+ }
366
+
367
+ a {
368
+ color: var(--color-primary-dark);
369
+ }
370
+
371
+ .attachments {
372
+ display: flex;
373
+ flex-direction: row;
374
+ flex-wrap: wrap;
375
+ align-items: center;
376
+ align-self: flex-start;
377
+ }
378
+
379
+ .incoming .attachments {
380
+ align-self: flex-end;
381
+ }
382
+
383
+ temba-thumbnail {
384
+ margin: 0.4em;
385
+ border-radius: var(--curvature);
386
+ }
387
+
388
+ .error .bubble {
389
+ border: 1px solid var(--color-error);
390
+ background: white;
391
+ color: #333;
392
+ }
393
+
394
+ .error .bubble .name {
395
+ color: #999;
396
+ }
397
+
398
+ .error temba-thumbnail {
399
+ --thumb-background: var(--color-error);
400
+ --thumb-icon: white;
401
+ }
402
+
403
+ .popup {
404
+ align-self: center;
405
+ display: flex;
406
+ position: absolute;
407
+ background: #fff;
408
+ padding: 0.5em 1em;
409
+ justify-content: center;
410
+ text-align: center;
411
+ border-radius: var(--curvature);
412
+ box-shadow: rgba(0, 0, 0, 0.05) 0px 3px 7px 0px,
413
+ rgba(0, 0, 0, 0.2) 0px 1px 2px 0px;
414
+ border: 1px solid #f3f3f3;
415
+ opacity: 0;
416
+ transform: scale(0.8);
417
+ transition: opacity 0.2s ease-out, transform 0.2s ease-out;
418
+ z-index: 2;
419
+ }
420
+
421
+ .popup .arrow {
422
+ z-index: 1;
423
+ text-shadow: 0px 3px 3px rgba(0, 0, 0, 0.1);
424
+ position: absolute;
425
+ justify-content: center;
426
+ text-align: center;
427
+ font-size: 1.3em;
428
+ transform: translateY(0.7em) scale(1);
429
+ color: #fff;
430
+ bottom: 0;
431
+ }
432
+
433
+ .bubble-wrap:hover .popup {
434
+ transform: translateY(-100%);
435
+ margin-top: -0.5em;
436
+ opacity: 1;
437
+ }
438
+ `;
439
+ }
440
+ firstUpdated(changed) {
441
+ super.firstUpdated(changed);
442
+ const scroll = this.shadowRoot.querySelector('.scroll');
443
+ const hasScroll = scroll.scrollHeight > scroll.clientHeight;
444
+ this.hideBottomScroll = true;
445
+ this.hideTopScroll = !hasScroll;
446
+ }
447
+ addMessages(messages, startTime = null, append = false) {
448
+ // make sure our messages have ids
449
+ messages.forEach((m) => {
450
+ if (!m.id) {
451
+ m.id = hashCode(m.text) + '_' + m.date.toISOString();
452
+ }
453
+ });
454
+ if (!startTime) {
455
+ startTime = new Date();
456
+ }
457
+ const elapsed = new Date().getTime() - startTime.getTime();
458
+ window.setTimeout(() => {
459
+ this.fetching = false;
460
+ // first add messages to the map
461
+ const newMessages = [];
462
+ for (const m of messages) {
463
+ if (this.addMessage(m)) {
464
+ newMessages.push(m.id);
465
+ }
466
+ }
467
+ if (newMessages.length === 0) {
468
+ return;
469
+ }
470
+ const ele = this.shadowRoot.querySelector('.scroll');
471
+ const prevTop = ele.scrollTop;
472
+ const grouped = this.groupMessages(newMessages);
473
+ this.insertGroups(grouped, append);
474
+ window.setTimeout(() => {
475
+ ele.scrollTop = prevTop;
476
+ this.fireCustomEvent(CustomEventType.FetchComplete);
477
+ }, 100);
478
+ },
479
+ // if it's the first load don't wait, otherwise wait a minimum amount of time
480
+ this.messageGroups.length === 0
481
+ ? 0
482
+ : Math.max(0, MIN_FETCH_TIME - elapsed));
483
+ }
484
+ addMessage(msg) {
485
+ const isNew = !this.messageExists(msg);
486
+ this.msgMap.set(msg.id, msg);
487
+ return isNew;
488
+ }
489
+ messageExists(msg) {
490
+ return this.msgMap.has(msg.id);
491
+ }
492
+ isSameGroup(msg1, msg2) {
493
+ var _a, _b;
494
+ if (msg1 && msg2) {
495
+ return (msg1.type === msg2.type &&
496
+ ((_a = msg1.user) === null || _a === void 0 ? void 0 : _a.name) === ((_b = msg2.user) === null || _b === void 0 ? void 0 : _b.name) &&
497
+ Math.abs(msg1.date.getTime() - msg2.date.getTime()) < BATCH_TIME_WINDOW);
498
+ }
499
+ return false;
500
+ }
501
+ insertGroups(newGroups, append = false) {
502
+ if (!append) {
503
+ newGroups.reverse();
504
+ }
505
+ for (const newGroup of newGroups) {
506
+ // see if our new group belongs to the most recent group
507
+ const group = this.messageGroups[append ? 0 : this.messageGroups.length - 1];
508
+ if (group) {
509
+ const lastMsgId = group[group.length - 1];
510
+ const lastMsg = this.msgMap.get(lastMsgId);
511
+ const newMsg = this.msgMap.get(newGroup[0]);
512
+ // if our message belongs to the previous group, in we go
513
+ if (this.isSameGroup(lastMsg, newMsg)) {
514
+ group.push(...newGroup);
515
+ }
516
+ else {
517
+ // otherwise, just add our entire group as a new one
518
+ if (append) {
519
+ this.messageGroups.splice(0, 0, newGroup);
520
+ }
521
+ else {
522
+ this.messageGroups.push(newGroup);
523
+ }
524
+ }
525
+ }
526
+ else {
527
+ if (append) {
528
+ this.messageGroups.splice(0, 0, newGroup);
529
+ }
530
+ else {
531
+ this.messageGroups.push(newGroup);
532
+ }
533
+ }
534
+ }
535
+ this.requestUpdate('messageGroups');
536
+ }
537
+ groupMessages(msgIds) {
538
+ // group our messages by origin and user
539
+ const groups = [];
540
+ let lastGroup = [];
541
+ let lastMsg = null;
542
+ for (const msgId of msgIds) {
543
+ const msg = this.msgMap.get(msgId);
544
+ if (!this.isSameGroup(msg, lastMsg)) {
545
+ lastGroup = [];
546
+ groups.push(lastGroup);
547
+ }
548
+ lastGroup.push(msgId);
549
+ lastMsg = msg;
550
+ }
551
+ return groups;
552
+ }
553
+ handleScroll(event) {
554
+ const ele = event.target;
555
+ const top = ele.scrollHeight - ele.clientHeight;
556
+ const scroll = Math.round(top + ele.scrollTop);
557
+ const scrollPct = scroll / top;
558
+ this.hideTopScroll = scrollPct <= 0.01;
559
+ this.hideBottomScroll = scrollPct >= 0.99;
560
+ if (scrollPct < SCROLL_FETCH_BUFFER) {
561
+ this.fireCustomEvent(CustomEventType.ScrollThreshold);
562
+ }
563
+ }
564
+ scrollToBottom() {
565
+ const scroll = this.shadowRoot.querySelector('.scroll');
566
+ if (scroll) {
567
+ scroll.scrollTop = scroll.scrollHeight;
568
+ this.hideBottomScroll = true;
569
+ }
570
+ }
571
+ renderMessageGroup(msgIds, idx, groups) {
572
+ var _a, _b;
573
+ const today = new Date();
574
+ const firstGroup = idx === groups.length - 1;
575
+ let prevMsg;
576
+ if (idx > 0) {
577
+ const lastGroup = groups[idx - 1];
578
+ if (lastGroup && lastGroup.length > 0) {
579
+ prevMsg = this.msgMap.get(lastGroup[0]);
580
+ }
581
+ }
582
+ const mostRecentId = msgIds[msgIds.length - 1];
583
+ const currentMsg = this.msgMap.get(mostRecentId);
584
+ let timeDisplay = null;
585
+ if (prevMsg &&
586
+ !this.isSameGroup(prevMsg, currentMsg) &&
587
+ (Math.abs(currentMsg.date.getTime() - prevMsg.date.getTime()) >
588
+ BATCH_TIME_WINDOW ||
589
+ idx === groups.length - 1)) {
590
+ if (today.getDate() !== prevMsg.date.getDate() ||
591
+ prevMsg.date.getDate() !== currentMsg.date.getDate()) {
592
+ timeDisplay = html `<div class="time ${firstGroup ? 'first' : ''}">
593
+ ${prevMsg.date.toLocaleTimeString(undefined, VERBOSE_FORMAT)}
594
+ </div>`;
595
+ }
596
+ else {
597
+ timeDisplay = html `<div class="time ${firstGroup ? 'first' : ''}">
598
+ ${prevMsg.date.toLocaleTimeString(undefined, TIME_FORMAT)}
599
+ </div>`;
600
+ }
601
+ }
602
+ const incoming = this.agent
603
+ ? currentMsg.type !== 'msg_in'
604
+ : currentMsg.type === 'msg_in';
605
+ const name = (_a = currentMsg.user) === null || _a === void 0 ? void 0 : _a.name;
606
+ let avatar = (_b = currentMsg.user) === null || _b === void 0 ? void 0 : _b.avatar;
607
+ if (!currentMsg.user) {
608
+ avatar = this.defaultAvatar;
609
+ }
610
+ let showAvatar = ((currentMsg.type === 'note' ||
611
+ currentMsg.type === 'msg_in' ||
612
+ currentMsg.type === 'msg_out') &&
613
+ this.agent) ||
614
+ !incoming;
615
+ // if we don't have a name or avatar, skip it
616
+ showAvatar = showAvatar && (!!avatar || !!name);
617
+ return html `
618
+ ${!firstGroup ? timeDisplay : null}
619
+ <div
620
+ class="block ${incoming ? 'incoming' : 'outgoing'} ${currentMsg.type}"
621
+ >
622
+ ${msgIds.slice(0, msgIds.length - 1).map((msgId, index) => {
623
+ const msg = this.msgMap.get(msgId);
624
+ return html `<div class="row message">
625
+ ${showAvatar ? html `<div class="avatar"></div>` : null}
626
+ ${this.renderMessage(msg, index == 0 ? name : null)}
627
+ </div>`;
628
+ })}
629
+ <div class="row latest message">
630
+ ${showAvatar
631
+ ? html `<div class="avatar">
632
+ ${renderAvatar({ name: name, user: { avatar: avatar } })}
633
+ </div>`
634
+ : null}
635
+ ${this.renderMessage(currentMsg, showAvatar && msgIds.length === 1 ? name : null)}
636
+ </div>
637
+ </div>
638
+ ${firstGroup ? timeDisplay : null}
639
+ `;
640
+ }
641
+ renderMessage(event, name = null) {
642
+ if (event.type === MessageType.Error ||
643
+ event.type === MessageType.Collapse ||
644
+ event.type === MessageType.Inline) {
645
+ return html `<div class="event">${renderMarkdown(event.text)}</div>`;
646
+ }
647
+ const message = event;
648
+ return html `
649
+ <div class="bubble-wrap ${message.sendError ? 'error' : ''}">
650
+ ${message.popup
651
+ ? html `<div class="popup">
652
+ ${message.popup}
653
+ <div class="arrow">▼</div>
654
+ </div>`
655
+ : null}
656
+
657
+ ${message.text
658
+ ? html `
659
+ <div class="bubble">
660
+ ${name ? html `<div class="name">${name}</div>` : null}
661
+ <div class="message">${message.text}</div>
662
+
663
+ <!--div>${message.date.toLocaleDateString(undefined, VERBOSE_FORMAT)}</div-->
664
+ </div>
665
+ `
666
+ : null}
667
+
668
+ <div class="attachments">
669
+ ${(message.attachments || []).map((attachment) => html `<temba-thumbnail
670
+ attachment="${attachment}"
671
+ ></temba-thumbnail>`)}
672
+ </div>
673
+ </div>
674
+ </div>
675
+ `;
676
+ }
677
+ render() {
678
+ return html ` <div
679
+ class="
680
+ messages
681
+ ${this.hideBottomScroll ? 'scroll-at-bottom' : ''}
682
+ ${this.hideTopScroll ? 'scroll-at-top' : ''}"
683
+ >
684
+ <div class="scroll" @scroll=${this.handleScroll}>
685
+ ${this.messageGroups
686
+ ? this.messageGroups.map((msgGroup, idx, groups) => html `${this.renderMessageGroup(msgGroup, idx, groups)}`)
687
+ : null}
688
+
689
+ <temba-loading
690
+ class="${!this.fetching ? 'hidden' : ''}"
691
+ ></temba-loading>
692
+ </div>
693
+ </div>`;
694
+ }
695
+ }
696
+ __decorate([
697
+ property({ type: Array })
698
+ ], Chat.prototype, "messageGroups", void 0);
699
+ __decorate([
700
+ property({ type: Boolean })
701
+ ], Chat.prototype, "fetching", void 0);
702
+ __decorate([
703
+ property({ type: Boolean, attribute: false })
704
+ ], Chat.prototype, "hideTopScroll", void 0);
705
+ __decorate([
706
+ property({ type: Boolean, attribute: false })
707
+ ], Chat.prototype, "hideBottomScroll", void 0);
708
+ __decorate([
709
+ property({ type: String, attribute: 'avatar' })
710
+ ], Chat.prototype, "defaultAvatar", void 0);
711
+ __decorate([
712
+ property({ type: Boolean })
713
+ ], Chat.prototype, "agent", void 0);
714
+ //# sourceMappingURL=Chat.js.map