@nyaruka/temba-components 0.132.0 → 0.134.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.
- package/CHANGELOG.md +31 -1
- package/demo/components/flow/example.html +1 -0
- package/demo/components/webchat/example.html +1 -1
- package/demo/static/css/tailwind.css +30019 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +2 -11
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +555 -476
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/Chat.js +248 -95
- package/out-tsc/src/display/Chat.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +4 -4
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/display/TembaUser.js +3 -3
- package/out-tsc/src/display/TembaUser.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +132 -58
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +183 -58
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/utils.js +141 -0
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/FloatingWindow.js +1 -2
- package/out-tsc/src/layout/FloatingWindow.js.map +1 -1
- package/out-tsc/src/list/ContentMenu.js +1 -0
- package/out-tsc/src/list/ContentMenu.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +3 -2
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +184 -205
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +2 -11
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/store/AppState.js +34 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/store/Store.js +5 -5
- package/out-tsc/src/store/Store.js.map +1 -1
- package/out-tsc/src/utils.js +3 -3
- package/out-tsc/src/utils.js.map +1 -1
- package/out-tsc/src/webchat/WebChat.js +22 -9
- package/out-tsc/src/webchat/WebChat.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +6 -5
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/actions/send_broadcast.test.js +9 -4
- package/out-tsc/test/actions/send_broadcast.test.js.map +1 -1
- package/out-tsc/test/temba-contact-chat.test.js +1 -1
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-floating-window.test.js +0 -2
- package/out-tsc/test/temba-floating-window.test.js.map +1 -1
- package/out-tsc/test/temba-flow-collision.test.js +673 -0
- package/out-tsc/test/temba-flow-collision.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +195 -0
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-utils-uuid.test.js +45 -1
- package/out-tsc/test/temba-utils-uuid.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +2 -2
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
- package/screenshots/truth/contacts/chat-failure.png +0 -0
- package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
- package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
- package/screenshots/truth/floating-tab/default.png +0 -0
- package/screenshots/truth/floating-tab/gray.png +0 -0
- package/screenshots/truth/floating-tab/green.png +0 -0
- package/screenshots/truth/floating-tab/hover.png +0 -0
- package/screenshots/truth/floating-tab/purple.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/display/Chat.ts +331 -135
- package/src/display/FloatingTab.ts +4 -4
- package/src/display/TembaUser.ts +3 -2
- package/src/events.ts +12 -12
- package/src/flow/CanvasNode.ts +140 -57
- package/src/flow/Editor.ts +240 -58
- package/src/flow/utils.ts +207 -1
- package/src/interfaces.ts +7 -0
- package/src/layout/FloatingWindow.ts +1 -3
- package/src/list/ContentMenu.ts +1 -0
- package/src/list/SortableList.ts +3 -2
- package/src/live/ContactChat.ts +195 -221
- package/src/locales/es.ts +13 -18
- package/src/locales/fr.ts +13 -18
- package/src/locales/locale-codes.ts +2 -11
- package/src/locales/pt.ts +13 -18
- package/src/store/AppState.ts +43 -0
- package/src/store/Store.ts +5 -5
- package/src/utils.ts +3 -3
- package/src/webchat/WebChat.ts +24 -10
- package/test/ActionHelper.ts +13 -5
- package/test/actions/send_broadcast.test.ts +4 -2
- package/test/temba-contact-chat.test.ts +1 -1
- package/test/temba-floating-window.test.ts +0 -2
- package/test/temba-flow-collision.test.ts +833 -0
- package/test/temba-flow-editor-node.test.ts +224 -0
- package/test/temba-utils-uuid.test.ts +61 -1
- package/test/utils.test.ts +7 -2
- package/test-assets/contacts/history.json +22 -9
- package/web-test-runner.config.mjs +3 -3
|
@@ -1,21 +1,49 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { html, css } from 'lit';
|
|
3
3
|
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
4
5
|
import { RapidElement } from '../RapidElement';
|
|
5
6
|
import { CustomEventType } from '../interfaces';
|
|
6
7
|
import { DEFAULT_AVATAR } from '../webchat/assets';
|
|
7
|
-
import { hashCode } from '../utils';
|
|
8
8
|
const BATCH_TIME_WINDOW = 60 * 60 * 1000;
|
|
9
|
-
const SCROLL_FETCH_BUFFER =
|
|
9
|
+
const SCROLL_FETCH_BUFFER = 200; // pixels from top
|
|
10
10
|
const MIN_FETCH_TIME = 250;
|
|
11
|
+
const getUnsendableReasonMessage = (reason) => {
|
|
12
|
+
switch (reason) {
|
|
13
|
+
case 'no_route':
|
|
14
|
+
return 'No channel available to send message';
|
|
15
|
+
case 'contact_blocked':
|
|
16
|
+
return 'Contact has been blocked';
|
|
17
|
+
case 'contact_stopped':
|
|
18
|
+
return 'Contact has been stopped';
|
|
19
|
+
case 'contact_archived':
|
|
20
|
+
return 'Contact is archived';
|
|
21
|
+
case 'org_suspended':
|
|
22
|
+
return 'Workspace is suspended';
|
|
23
|
+
case 'looping':
|
|
24
|
+
return 'Message loop detected';
|
|
25
|
+
default:
|
|
26
|
+
return 'Unable to send message';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const getStatusReasonMessage = (reason) => {
|
|
30
|
+
switch (reason) {
|
|
31
|
+
case 'error_limit':
|
|
32
|
+
return 'Error limit reached';
|
|
33
|
+
case 'too_old':
|
|
34
|
+
return 'Message is too old to send';
|
|
35
|
+
case 'channel_removed':
|
|
36
|
+
return 'Channel was removed';
|
|
37
|
+
default:
|
|
38
|
+
return 'Message failed to send';
|
|
39
|
+
}
|
|
40
|
+
};
|
|
11
41
|
export var MessageType;
|
|
12
42
|
(function (MessageType) {
|
|
13
43
|
MessageType["Inline"] = "inline";
|
|
14
44
|
MessageType["Error"] = "error";
|
|
15
45
|
MessageType["Collapse"] = "collapse";
|
|
16
46
|
MessageType["Note"] = "note";
|
|
17
|
-
MessageType["MsgIn"] = "msg_in";
|
|
18
|
-
MessageType["MsgOut"] = "msg_out";
|
|
19
47
|
})(MessageType || (MessageType = {}));
|
|
20
48
|
const TIME_FORMAT = { hour: 'numeric', minute: '2-digit' };
|
|
21
49
|
const VERBOSE_FORMAT = {
|
|
@@ -35,7 +63,12 @@ export class Chat extends RapidElement {
|
|
|
35
63
|
this.hideBottomScroll = true;
|
|
36
64
|
this.defaultAvatar = DEFAULT_AVATAR;
|
|
37
65
|
this.agent = false;
|
|
66
|
+
this.endOfHistory = false;
|
|
67
|
+
this.oldestEventDate = null;
|
|
68
|
+
this.showNewMessageNotification = false;
|
|
69
|
+
this.hasFooter = false;
|
|
38
70
|
this.msgMap = new Map();
|
|
71
|
+
this.metadataCache = new Map();
|
|
39
72
|
}
|
|
40
73
|
static get styles() {
|
|
41
74
|
return css `
|
|
@@ -92,7 +125,8 @@ export class Chat extends RapidElement {
|
|
|
92
125
|
text-align: center;
|
|
93
126
|
font-size: 0.8em;
|
|
94
127
|
color: #999;
|
|
95
|
-
margin-
|
|
128
|
+
margin-bottom: 2em;
|
|
129
|
+
margin-top: 1em;
|
|
96
130
|
border-top: 1px solid #e9e9e9;
|
|
97
131
|
padding: 1em;
|
|
98
132
|
margin-left: 10%;
|
|
@@ -204,16 +238,24 @@ export class Chat extends RapidElement {
|
|
|
204
238
|
color: rgba(0, 0, 0, 0.5);
|
|
205
239
|
}
|
|
206
240
|
|
|
241
|
+
.failed .bubble,
|
|
242
|
+
.error .bubble {
|
|
243
|
+
border: 1px solid var(--color-error);
|
|
244
|
+
background: #ffe6e6;
|
|
245
|
+
color: #ad4747ff;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.error .bubble .name,
|
|
249
|
+
.failed .bubble .name {
|
|
250
|
+
color: #ad47479a;
|
|
251
|
+
}
|
|
252
|
+
|
|
207
253
|
.message {
|
|
208
254
|
margin-bottom: 0.5em;
|
|
209
255
|
line-height: 1.2em;
|
|
210
256
|
word-break: break-word;
|
|
211
257
|
}
|
|
212
258
|
|
|
213
|
-
.message-text {
|
|
214
|
-
white-space: pre-line;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
259
|
.chat {
|
|
218
260
|
width: 28rem;
|
|
219
261
|
border-radius: var(--curvature);
|
|
@@ -424,19 +466,12 @@ export class Chat extends RapidElement {
|
|
|
424
466
|
border-radius: var(--curvature);
|
|
425
467
|
}
|
|
426
468
|
|
|
427
|
-
.
|
|
428
|
-
border: 1px solid var(--color-error);
|
|
429
|
-
background: white;
|
|
430
|
-
color: #333;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
.error .bubble .name {
|
|
434
|
-
color: #999;
|
|
435
|
-
}
|
|
436
|
-
|
|
469
|
+
.failed temba-thumbnail,
|
|
437
470
|
.error temba-thumbnail {
|
|
438
|
-
--thumb-background:
|
|
439
|
-
--thumb-
|
|
471
|
+
--thumb-background: #ffe6e6;
|
|
472
|
+
--thumb-border: var(--color-error);
|
|
473
|
+
border: 1px solid var(--color-error);
|
|
474
|
+
color: #ad4747a8;
|
|
440
475
|
}
|
|
441
476
|
|
|
442
477
|
.outgoing .popup {
|
|
@@ -480,6 +515,37 @@ export class Chat extends RapidElement {
|
|
|
480
515
|
opacity: 1;
|
|
481
516
|
transition-delay: 1s;
|
|
482
517
|
}
|
|
518
|
+
|
|
519
|
+
.new-message-notification {
|
|
520
|
+
position: absolute;
|
|
521
|
+
bottom: 1em;
|
|
522
|
+
left: 50%;
|
|
523
|
+
transform: translateX(-50%) translateY(100px);
|
|
524
|
+
background: var(--color-primary-dark, #3c92dd);
|
|
525
|
+
color: white;
|
|
526
|
+
padding: 0.75em 1.5em;
|
|
527
|
+
border-radius: var(--curvature);
|
|
528
|
+
box-shadow: rgba(0, 0, 0, 0.2) 0px 3px 7px 0px,
|
|
529
|
+
rgba(0, 0, 0, 0.3) 0px 1px 2px 0px;
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
opacity: 0;
|
|
532
|
+
transition: all 0.3s ease-out;
|
|
533
|
+
z-index: 100;
|
|
534
|
+
font-weight: 500;
|
|
535
|
+
pointer-events: none;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.new-message-notification.visible {
|
|
539
|
+
transform: translateX(-50%) translateY(0);
|
|
540
|
+
opacity: 1;
|
|
541
|
+
pointer-events: auto;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.new-message-notification:hover {
|
|
545
|
+
background: var(--color-primary-darker, #2b7ac4);
|
|
546
|
+
box-shadow: rgba(0, 0, 0, 0.3) 0px 4px 10px 0px,
|
|
547
|
+
rgba(0, 0, 0, 0.4) 0px 2px 4px 0px;
|
|
548
|
+
}
|
|
483
549
|
`;
|
|
484
550
|
}
|
|
485
551
|
firstUpdated(changed) {
|
|
@@ -490,15 +556,6 @@ export class Chat extends RapidElement {
|
|
|
490
556
|
this.hideTopScroll = !hasScroll;
|
|
491
557
|
}
|
|
492
558
|
addMessages(messages, startTime = null, append = false) {
|
|
493
|
-
// make sure our messages have ids
|
|
494
|
-
messages.forEach((m) => {
|
|
495
|
-
if (!m.id) {
|
|
496
|
-
m.id =
|
|
497
|
-
hashCode((m.text.strings || []).join('')) +
|
|
498
|
-
'_' +
|
|
499
|
-
m.date.toISOString();
|
|
500
|
-
}
|
|
501
|
-
});
|
|
502
559
|
if (!startTime) {
|
|
503
560
|
startTime = new Date();
|
|
504
561
|
}
|
|
@@ -508,8 +565,16 @@ export class Chat extends RapidElement {
|
|
|
508
565
|
// first add messages to the map
|
|
509
566
|
const newMessages = [];
|
|
510
567
|
for (const m of messages) {
|
|
568
|
+
// filter out metadata events - they aren't rendered but cached for later reference
|
|
569
|
+
if (m.type === 'msg_deleted' || m.type === 'msg_status_changed') {
|
|
570
|
+
const msgUuid = m.msg_uuid;
|
|
571
|
+
if (msgUuid) {
|
|
572
|
+
this.metadataCache.set(msgUuid, m);
|
|
573
|
+
}
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
511
576
|
if (this.addMessage(m)) {
|
|
512
|
-
newMessages.push(m.
|
|
577
|
+
newMessages.push(m.uuid);
|
|
513
578
|
}
|
|
514
579
|
}
|
|
515
580
|
if (newMessages.length === 0) {
|
|
@@ -517,10 +582,25 @@ export class Chat extends RapidElement {
|
|
|
517
582
|
}
|
|
518
583
|
const ele = this.shadowRoot.querySelector('.scroll');
|
|
519
584
|
const prevTop = ele.scrollTop;
|
|
585
|
+
const prevScrollHeight = ele.scrollHeight;
|
|
586
|
+
const scrollableHeight = ele.scrollHeight - ele.clientHeight;
|
|
587
|
+
const isScrolledAway = scrollableHeight > 0 && Math.abs(ele.scrollTop) > 50;
|
|
520
588
|
const grouped = this.groupMessages(newMessages);
|
|
521
589
|
this.insertGroups(grouped, append);
|
|
590
|
+
// show notification if new messages are appended and user is scrolled away from bottom
|
|
591
|
+
if (append && isScrolledAway && newMessages.length > 0) {
|
|
592
|
+
this.showNewMessageNotification = true;
|
|
593
|
+
}
|
|
522
594
|
window.setTimeout(() => {
|
|
523
|
-
|
|
595
|
+
// when appending (new messages at bottom), adjust scroll to maintain visible content
|
|
596
|
+
// with column-reverse, new content at bottom increases scrollHeight
|
|
597
|
+
if (append && isScrolledAway) {
|
|
598
|
+
const heightDiff = ele.scrollHeight - prevScrollHeight;
|
|
599
|
+
ele.scrollTop = prevTop - heightDiff;
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
ele.scrollTop = prevTop;
|
|
603
|
+
}
|
|
524
604
|
this.fireCustomEvent(CustomEventType.FetchComplete);
|
|
525
605
|
}, 100);
|
|
526
606
|
},
|
|
@@ -531,18 +611,20 @@ export class Chat extends RapidElement {
|
|
|
531
611
|
}
|
|
532
612
|
addMessage(msg) {
|
|
533
613
|
const isNew = !this.messageExists(msg);
|
|
534
|
-
this.msgMap.set(msg.
|
|
614
|
+
this.msgMap.set(msg.uuid, msg);
|
|
535
615
|
return isNew;
|
|
536
616
|
}
|
|
537
617
|
messageExists(msg) {
|
|
538
|
-
return this.msgMap.has(msg.
|
|
618
|
+
return this.msgMap.has(msg.uuid);
|
|
539
619
|
}
|
|
540
620
|
isSameGroup(msg1, msg2) {
|
|
541
621
|
var _a, _b;
|
|
542
622
|
if (msg1 && msg2) {
|
|
543
|
-
|
|
544
|
-
((_a = msg1.
|
|
545
|
-
Math.abs(msg1.
|
|
623
|
+
const sameGroup = msg1.type === msg2.type &&
|
|
624
|
+
((_a = msg1._user) === null || _a === void 0 ? void 0 : _a.name) === ((_b = msg2._user) === null || _b === void 0 ? void 0 : _b.name) &&
|
|
625
|
+
Math.abs(msg1.created_on.getTime() - msg2.created_on.getTime()) <
|
|
626
|
+
BATCH_TIME_WINDOW;
|
|
627
|
+
return sameGroup;
|
|
546
628
|
}
|
|
547
629
|
return false;
|
|
548
630
|
}
|
|
@@ -600,24 +682,39 @@ export class Chat extends RapidElement {
|
|
|
600
682
|
}
|
|
601
683
|
handleScroll(event) {
|
|
602
684
|
const ele = event.target;
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
if (
|
|
685
|
+
const scrollableHeight = ele.scrollHeight - ele.clientHeight;
|
|
686
|
+
if (scrollableHeight <= 0) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
// with column-reverse, scrollTop behavior depends on the browser
|
|
690
|
+
// check if scrollTop is negative (some browsers) or positive (others)
|
|
691
|
+
const absScrollTop = Math.abs(ele.scrollTop);
|
|
692
|
+
// when scrolling up to older messages, absScrollTop increases
|
|
693
|
+
// trigger when we're close to the maximum scroll (oldest messages)
|
|
694
|
+
const shouldFetch = absScrollTop >= scrollableHeight - SCROLL_FETCH_BUFFER;
|
|
695
|
+
this.hideTopScroll = absScrollTop >= scrollableHeight - 1;
|
|
696
|
+
this.hideBottomScroll = absScrollTop <= 1;
|
|
697
|
+
// hide notification when scrolled to bottom
|
|
698
|
+
if (absScrollTop <= 10) {
|
|
699
|
+
this.showNewMessageNotification = false;
|
|
700
|
+
}
|
|
701
|
+
if (shouldFetch) {
|
|
609
702
|
this.fireCustomEvent(CustomEventType.ScrollThreshold);
|
|
610
703
|
}
|
|
611
704
|
}
|
|
612
705
|
scrollToBottom() {
|
|
613
706
|
const scroll = this.shadowRoot.querySelector('.scroll');
|
|
614
707
|
if (scroll) {
|
|
615
|
-
scroll.scrollTop =
|
|
708
|
+
scroll.scrollTop = 0;
|
|
616
709
|
this.hideBottomScroll = true;
|
|
710
|
+
this.showNewMessageNotification = false;
|
|
617
711
|
}
|
|
618
712
|
}
|
|
713
|
+
handleNewMessageClick() {
|
|
714
|
+
this.scrollToBottom();
|
|
715
|
+
}
|
|
619
716
|
renderMessageGroup(msgIds, idx, groups) {
|
|
620
|
-
var _a, _b, _c;
|
|
717
|
+
var _a, _b, _c, _d;
|
|
621
718
|
const today = new Date();
|
|
622
719
|
const firstGroup = idx === groups.length - 1;
|
|
623
720
|
let prevMsg;
|
|
@@ -632,91 +729,113 @@ export class Chat extends RapidElement {
|
|
|
632
729
|
let timeDisplay = null;
|
|
633
730
|
if (prevMsg &&
|
|
634
731
|
!this.isSameGroup(prevMsg, currentMsg) &&
|
|
635
|
-
(Math.abs(currentMsg.
|
|
636
|
-
BATCH_TIME_WINDOW ||
|
|
732
|
+
(Math.abs(currentMsg.created_on.getTime() - prevMsg.created_on.getTime()) > BATCH_TIME_WINDOW ||
|
|
637
733
|
idx === groups.length - 1)) {
|
|
638
|
-
if (today.getDate() !== prevMsg.
|
|
639
|
-
prevMsg.
|
|
734
|
+
if (today.getDate() !== prevMsg.created_on.getDate() ||
|
|
735
|
+
prevMsg.created_on.getDate() !== currentMsg.created_on.getDate()) {
|
|
640
736
|
timeDisplay = html `<div class="time ${firstGroup ? 'first' : ''}">
|
|
641
|
-
${prevMsg.
|
|
737
|
+
${prevMsg.created_on.toLocaleTimeString(undefined, VERBOSE_FORMAT)}
|
|
642
738
|
</div>`;
|
|
643
739
|
}
|
|
644
740
|
else {
|
|
645
741
|
timeDisplay = html `<div class="time ${firstGroup ? 'first' : ''}">
|
|
646
|
-
${prevMsg.
|
|
742
|
+
${prevMsg.created_on.toLocaleTimeString(undefined, TIME_FORMAT)}
|
|
647
743
|
</div>`;
|
|
648
744
|
}
|
|
649
745
|
}
|
|
650
746
|
const incoming = this.agent
|
|
651
|
-
? currentMsg.type !== '
|
|
652
|
-
: currentMsg.type === '
|
|
653
|
-
const name = (_a = currentMsg.
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
currentMsg.type === 'msg_in' ||
|
|
657
|
-
currentMsg.type === 'msg_out') &&
|
|
747
|
+
? currentMsg.type !== 'msg_received'
|
|
748
|
+
: currentMsg.type === 'msg_received';
|
|
749
|
+
const name = (_a = currentMsg._user) === null || _a === void 0 ? void 0 : _a.name;
|
|
750
|
+
const showAvatar = ((currentMsg.type === 'msg_received' ||
|
|
751
|
+
currentMsg.type === 'msg_created') &&
|
|
658
752
|
this.agent) ||
|
|
659
753
|
!incoming;
|
|
754
|
+
const isSystem = !((_b = currentMsg._user) === null || _b === void 0 ? void 0 : _b.uuid);
|
|
660
755
|
return html `
|
|
661
|
-
${
|
|
662
|
-
<div
|
|
663
|
-
class="block ${incoming ? 'incoming' : 'outgoing'} ${currentMsg.type}"
|
|
664
|
-
>
|
|
756
|
+
${timeDisplay}
|
|
757
|
+
<div class="block ${incoming ? 'incoming' : 'outgoing'}">
|
|
665
758
|
<div class="group-messages" style="flex-grow:1">
|
|
666
|
-
${msgIds
|
|
759
|
+
${repeat(msgIds, (msgId) => msgId, (msgId, index) => {
|
|
760
|
+
var _a, _b;
|
|
667
761
|
const msg = this.msgMap.get(msgId);
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
762
|
+
const msgEvent = msg;
|
|
763
|
+
const statusClass = msg._status
|
|
764
|
+
? msg._status.status
|
|
765
|
+
: '';
|
|
766
|
+
const hasError = ((_a = msgEvent.msg) === null || _a === void 0 ? void 0 : _a.unsendable_reason) ||
|
|
767
|
+
(((_b = msgEvent._status) === null || _b === void 0 ? void 0 : _b.reason) &&
|
|
768
|
+
(statusClass === 'failed' || statusClass === 'errored'));
|
|
769
|
+
const unsendableClass = hasError ? 'error' : '';
|
|
770
|
+
return html `<div
|
|
771
|
+
class="row message ${statusClass} ${unsendableClass}"
|
|
772
|
+
>
|
|
773
|
+
${this.renderMessage(msg, index == 0 ? name : null)}
|
|
774
|
+
</div>`;
|
|
671
775
|
})}
|
|
672
776
|
</div>
|
|
673
777
|
${showAvatar
|
|
674
778
|
? html `<div class="avatar" style="align-self:flex-end">
|
|
675
779
|
<temba-user
|
|
676
|
-
|
|
780
|
+
uuid=${(_c = currentMsg._user) === null || _c === void 0 ? void 0 : _c.uuid}
|
|
677
781
|
name=${name}
|
|
678
|
-
avatar=${(
|
|
679
|
-
?system=${
|
|
782
|
+
avatar=${(_d = currentMsg._user) === null || _d === void 0 ? void 0 : _d.avatar}
|
|
783
|
+
?system=${isSystem}
|
|
680
784
|
>
|
|
681
785
|
</temba-user>
|
|
682
786
|
</div>`
|
|
683
787
|
: null}
|
|
684
788
|
</div>
|
|
685
|
-
${firstGroup ? timeDisplay : null}
|
|
686
789
|
`;
|
|
687
790
|
}
|
|
688
791
|
renderMessage(event, name = null) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
return html `<div class="event">${event.text}</div>`;
|
|
792
|
+
var _a, _b;
|
|
793
|
+
if (event._rendered) {
|
|
794
|
+
return html `<div class="event">${event._rendered.html}</div>`;
|
|
693
795
|
}
|
|
694
796
|
const message = event;
|
|
797
|
+
const unsendableReason = (_a = message.msg) === null || _a === void 0 ? void 0 : _a.unsendable_reason;
|
|
798
|
+
const statusReason = (_b = message._status) === null || _b === void 0 ? void 0 : _b.reason;
|
|
799
|
+
const errorMessage = unsendableReason
|
|
800
|
+
? getUnsendableReasonMessage(unsendableReason)
|
|
801
|
+
: statusReason
|
|
802
|
+
? getStatusReasonMessage(statusReason)
|
|
803
|
+
: null;
|
|
695
804
|
return html `
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
805
|
+
<div class="bubble-wrap">
|
|
806
|
+
<div class="popup" style="white-space: nowrap;">
|
|
807
|
+
${errorMessage
|
|
808
|
+
? html `<div style="color: var(--color-error); margin-right: 1em;">
|
|
809
|
+
${errorMessage}
|
|
701
810
|
</div>`
|
|
702
811
|
: null}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
812
|
+
<temba-date
|
|
813
|
+
value="${message.created_on.toISOString()}"
|
|
814
|
+
display="relative"
|
|
815
|
+
></temba-date>
|
|
816
|
+
${message._logs_url
|
|
817
|
+
? html `<a
|
|
818
|
+
style="margin-left: 1em; color: var(--color-primary-dark);"
|
|
819
|
+
href="${message._logs_url}"
|
|
820
|
+
target="_blank"
|
|
821
|
+
rel="noopener noreferrer"
|
|
822
|
+
><temba-icon name="log"></temba-icon
|
|
823
|
+
></a>`
|
|
713
824
|
: null}
|
|
714
825
|
|
|
715
|
-
<div class="
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
826
|
+
<div class="arrow">▼</div>
|
|
827
|
+
</div>
|
|
828
|
+
${message.msg.text
|
|
829
|
+
? html `<div class="bubble">
|
|
830
|
+
${name ? html `<div class="name">${name}</div>` : null}
|
|
831
|
+
<div class="message message-text">${message.msg.text}</div>
|
|
832
|
+
</div>`
|
|
833
|
+
: null}
|
|
834
|
+
|
|
835
|
+
<div class="attachments">
|
|
836
|
+
${(message.msg.attachments || []).map((attachment) => html `<temba-thumbnail
|
|
837
|
+
attachment="${attachment}"
|
|
838
|
+
></temba-thumbnail>`)}
|
|
720
839
|
</div>
|
|
721
840
|
</div>
|
|
722
841
|
`;
|
|
@@ -726,6 +845,12 @@ export class Chat extends RapidElement {
|
|
|
726
845
|
this.messageGroups = [];
|
|
727
846
|
this.hideBottomScroll = true;
|
|
728
847
|
this.hideTopScroll = true;
|
|
848
|
+
this.endOfHistory = false;
|
|
849
|
+
this.oldestEventDate = null;
|
|
850
|
+
}
|
|
851
|
+
setEndOfHistory(oldestDate) {
|
|
852
|
+
this.endOfHistory = true;
|
|
853
|
+
this.oldestEventDate = oldestDate;
|
|
729
854
|
}
|
|
730
855
|
render() {
|
|
731
856
|
return html ` <div
|
|
@@ -736,13 +861,29 @@ export class Chat extends RapidElement {
|
|
|
736
861
|
>
|
|
737
862
|
<div class="scroll" @scroll=${this.handleScroll}>
|
|
738
863
|
${this.messageGroups
|
|
739
|
-
? this.messageGroups.
|
|
864
|
+
? repeat(this.messageGroups, (msgGroup) => msgGroup.join(','), (msgGroup, idx) => html `${this.renderMessageGroup(msgGroup, idx, this.messageGroups)}`)
|
|
740
865
|
: null}
|
|
741
866
|
|
|
742
867
|
<temba-loading
|
|
743
868
|
class="${!this.fetching ? 'hidden' : ''}"
|
|
744
869
|
></temba-loading>
|
|
870
|
+
|
|
871
|
+
${this.endOfHistory && this.oldestEventDate
|
|
872
|
+
? html `<div class="time first">
|
|
873
|
+
${this.oldestEventDate.toLocaleTimeString(undefined, VERBOSE_FORMAT)}
|
|
874
|
+
</div>`
|
|
875
|
+
: null}
|
|
745
876
|
</div>
|
|
877
|
+
${!this.hasFooter
|
|
878
|
+
? html `<div
|
|
879
|
+
class="new-message-notification ${this.showNewMessageNotification
|
|
880
|
+
? 'visible'
|
|
881
|
+
: ''}"
|
|
882
|
+
@click=${this.handleNewMessageClick}
|
|
883
|
+
>
|
|
884
|
+
New Messages
|
|
885
|
+
</div>`
|
|
886
|
+
: null}
|
|
746
887
|
<slot class="header" name="header"></slot>
|
|
747
888
|
<slot class="footer" name="footer"></slot>
|
|
748
889
|
</div>`;
|
|
@@ -766,4 +907,16 @@ __decorate([
|
|
|
766
907
|
__decorate([
|
|
767
908
|
property({ type: Boolean })
|
|
768
909
|
], Chat.prototype, "agent", void 0);
|
|
910
|
+
__decorate([
|
|
911
|
+
property({ type: Boolean, attribute: false })
|
|
912
|
+
], Chat.prototype, "endOfHistory", void 0);
|
|
913
|
+
__decorate([
|
|
914
|
+
property({ type: Object, attribute: false })
|
|
915
|
+
], Chat.prototype, "oldestEventDate", void 0);
|
|
916
|
+
__decorate([
|
|
917
|
+
property({ type: Boolean, attribute: false })
|
|
918
|
+
], Chat.prototype, "showNewMessageNotification", void 0);
|
|
919
|
+
__decorate([
|
|
920
|
+
property({ type: Boolean })
|
|
921
|
+
], Chat.prototype, "hasFooter", void 0);
|
|
769
922
|
//# sourceMappingURL=Chat.js.map
|