@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
|
@@ -3,7 +3,7 @@ import { __decorate } from "tslib";
|
|
|
3
3
|
import { css, html } from 'lit';
|
|
4
4
|
import { property } from 'lit/decorators.js';
|
|
5
5
|
import { CustomEventType } from '../interfaces';
|
|
6
|
-
import { fetchResults, getUrl, oxfordFn, postJSON, postUrl } from '../utils';
|
|
6
|
+
import { fetchResults, generateUUIDv7, getUrl, oxfordFn, postJSON, postUrl } from '../utils';
|
|
7
7
|
import { ContactStoreElement } from './ContactStoreElement';
|
|
8
8
|
import { MessageType } from '../display/Chat';
|
|
9
9
|
import { DEFAULT_AVATAR } from '../webchat/assets';
|
|
@@ -123,17 +123,37 @@ const renderContactURNsChanged = (event) => {
|
|
|
123
123
|
export const renderTicketAction = (event, action) => {
|
|
124
124
|
var _a;
|
|
125
125
|
const ticketUUID = ((_a = event.ticket) === null || _a === void 0 ? void 0 : _a.uuid) || event.ticket_uuid;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
const actionNote = event.note
|
|
127
|
+
? html `<div
|
|
128
|
+
style="width:85%; background: #fffac3; padding: 1em;margin-bottom: 1em 0; border: 1px solid #ffe97f;border-radius: var(--curvature);"
|
|
129
|
+
>
|
|
130
|
+
<div style="color: #8e830fff; font-size: 1em;margin-bottom:0.25em">
|
|
131
|
+
<strong>${event._user.name}</strong> added a note
|
|
132
|
+
<temba-date
|
|
133
|
+
value=${event.created_on.toISOString()}
|
|
134
|
+
display="relative"
|
|
135
|
+
></temba-date>
|
|
136
|
+
</div>
|
|
137
|
+
${event.note}
|
|
138
|
+
</div>`
|
|
139
|
+
: null;
|
|
140
|
+
if (action === 'noted') {
|
|
141
|
+
return html `${actionNote}`;
|
|
131
142
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
const description = event._user
|
|
144
|
+
? html `<div>
|
|
145
|
+
<strong>${event._user.name}</strong> ${action} a
|
|
146
|
+
<strong><a href="/ticket/all/closed/${ticketUUID}/">ticket</a></strong>
|
|
147
|
+
</div>`
|
|
148
|
+
: html `<div>
|
|
149
|
+
A
|
|
150
|
+
<strong><a href="/ticket/all/closed/${ticketUUID}/">ticket</a></strong>
|
|
151
|
+
was <strong>${action}</strong>
|
|
152
|
+
</div>`;
|
|
153
|
+
return html `<div style="${actionNote ? 'margin-bottom: 1em;' : ''}">
|
|
154
|
+
${description}
|
|
155
|
+
</div>
|
|
156
|
+
${actionNote}`;
|
|
137
157
|
};
|
|
138
158
|
export const renderTicketAssigneeChanged = (event) => {
|
|
139
159
|
if (event._user) {
|
|
@@ -427,10 +447,12 @@ export class ContactChat extends ContactStoreElement {
|
|
|
427
447
|
this.showInterrupt = false;
|
|
428
448
|
this.avatar = DEFAULT_AVATAR;
|
|
429
449
|
this.ticket = null;
|
|
430
|
-
this.
|
|
431
|
-
this.
|
|
450
|
+
this.beforeUUID = null; // for scrolling back through history
|
|
451
|
+
this.afterUUID = null; // for polling new messages
|
|
432
452
|
this.refreshId = null;
|
|
433
453
|
this.polling = false;
|
|
454
|
+
this.pollingInterval = 2000; // start at 2 seconds
|
|
455
|
+
this.lastFetchTime = null;
|
|
434
456
|
}
|
|
435
457
|
firstUpdated(changed) {
|
|
436
458
|
super.firstUpdated(changed);
|
|
@@ -446,6 +468,7 @@ export class ContactChat extends ContactStoreElement {
|
|
|
446
468
|
}
|
|
447
469
|
}
|
|
448
470
|
updated(changedProperties) {
|
|
471
|
+
var _a;
|
|
449
472
|
super.updated(changedProperties);
|
|
450
473
|
// if we don't have an endpoint infer one
|
|
451
474
|
if (changedProperties.has('data') ||
|
|
@@ -457,9 +480,15 @@ export class ContactChat extends ContactStoreElement {
|
|
|
457
480
|
}
|
|
458
481
|
this.currentContact = this.data;
|
|
459
482
|
}
|
|
460
|
-
if (changedProperties.has('currentContact')) {
|
|
483
|
+
if (changedProperties.has('currentContact') && this.currentContact) {
|
|
461
484
|
this.chat = this.shadowRoot.querySelector('temba-chat');
|
|
462
|
-
this.
|
|
485
|
+
if (this.currentContact.uuid !==
|
|
486
|
+
((_a = changedProperties.get('currentContact')) === null || _a === void 0 ? void 0 : _a.uuid)) {
|
|
487
|
+
this.reset();
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
setTimeout(() => this.checkForNewMessages(), 500);
|
|
491
|
+
}
|
|
463
492
|
this.fetchPreviousMessages();
|
|
464
493
|
}
|
|
465
494
|
}
|
|
@@ -469,10 +498,12 @@ export class ContactChat extends ContactStoreElement {
|
|
|
469
498
|
}
|
|
470
499
|
this.blockFetching = false;
|
|
471
500
|
this.ticket = null;
|
|
472
|
-
this.
|
|
473
|
-
this.
|
|
501
|
+
this.beforeUUID = null;
|
|
502
|
+
this.afterUUID = null;
|
|
474
503
|
this.refreshId = null;
|
|
475
504
|
this.polling = false;
|
|
505
|
+
this.pollingInterval = 2000;
|
|
506
|
+
this.lastFetchTime = null;
|
|
476
507
|
this.errorMessage = null;
|
|
477
508
|
const compose = this.shadowRoot.querySelector('temba-compose');
|
|
478
509
|
if (compose) {
|
|
@@ -511,8 +542,11 @@ export class ContactChat extends ContactStoreElement {
|
|
|
511
542
|
postJSON(`/contact/chat/${this.currentContact.uuid}/`, payload)
|
|
512
543
|
.then((response) => {
|
|
513
544
|
if (response.status < 400) {
|
|
514
|
-
const
|
|
515
|
-
|
|
545
|
+
const event = response.json.event;
|
|
546
|
+
event.created_on = new Date(event.created_on);
|
|
547
|
+
this.chat.addMessages([event], null, true);
|
|
548
|
+
// reset polling interval to 2 seconds after sending a message
|
|
549
|
+
this.pollingInterval = 2000;
|
|
516
550
|
this.checkForNewMessages();
|
|
517
551
|
composeEle.reset();
|
|
518
552
|
this.fireCustomEvent(CustomEventType.MessageSent, {
|
|
@@ -530,240 +564,174 @@ export class ContactChat extends ContactStoreElement {
|
|
|
530
564
|
}
|
|
531
565
|
getEndpoint() {
|
|
532
566
|
if (this.contact) {
|
|
533
|
-
return `/contact/
|
|
567
|
+
return `/contact/chat/${this.contact}/`;
|
|
534
568
|
}
|
|
535
569
|
return null;
|
|
536
570
|
}
|
|
537
|
-
scheduleRefresh() {
|
|
538
|
-
// knock five seconds off the newest event time so we are
|
|
539
|
-
// a little more aggressive about refreshing short term
|
|
540
|
-
let window = new Date().getTime() - this.newestEventTime / 1000 - 5000;
|
|
571
|
+
scheduleRefresh(hasNewEvents = false) {
|
|
541
572
|
if (this.refreshId) {
|
|
542
573
|
clearTimeout(this.refreshId);
|
|
543
574
|
this.refreshId = null;
|
|
544
575
|
}
|
|
545
|
-
//
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
576
|
+
// reset to 2 seconds if we received new events
|
|
577
|
+
if (hasNewEvents) {
|
|
578
|
+
this.pollingInterval = 2000;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
// increase interval by 1 second up to max of 15 seconds
|
|
582
|
+
this.pollingInterval = Math.min(this.pollingInterval + 1000, 15000);
|
|
583
|
+
}
|
|
549
584
|
this.refreshId = setTimeout(() => {
|
|
550
585
|
this.checkForNewMessages();
|
|
551
|
-
},
|
|
586
|
+
}, this.pollingInterval);
|
|
552
587
|
}
|
|
553
|
-
|
|
554
|
-
let message = null;
|
|
588
|
+
prerender(event) {
|
|
555
589
|
switch (event.type) {
|
|
556
590
|
case Events.AIRTIME_TRANSFERRED:
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
591
|
+
event._rendered = {
|
|
592
|
+
html: renderAirtimeTransferredEvent(event),
|
|
593
|
+
type: MessageType.Inline
|
|
560
594
|
};
|
|
561
595
|
break;
|
|
562
596
|
case Events.CALL_CREATED:
|
|
563
597
|
case Events.CALL_MISSED:
|
|
564
598
|
case Events.CALL_RECEIVED:
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
599
|
+
event._rendered = {
|
|
600
|
+
html: renderCallEvent(event),
|
|
601
|
+
type: MessageType.Inline
|
|
568
602
|
};
|
|
569
603
|
break;
|
|
570
604
|
case Events.CHAT_STARTED:
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
605
|
+
event._rendered = {
|
|
606
|
+
html: renderChatStartedEvent(event),
|
|
607
|
+
type: MessageType.Inline
|
|
574
608
|
};
|
|
575
609
|
break;
|
|
576
610
|
case Events.CONTACT_FIELD_CHANGED:
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
611
|
+
event._rendered = {
|
|
612
|
+
html: renderUpdateEvent(event),
|
|
613
|
+
type: MessageType.Inline
|
|
580
614
|
};
|
|
581
615
|
break;
|
|
582
616
|
case Events.CONTACT_GROUPS_CHANGED:
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
617
|
+
event._rendered = {
|
|
618
|
+
html: renderContactGroupsEvent(event),
|
|
619
|
+
type: MessageType.Inline
|
|
586
620
|
};
|
|
587
621
|
break;
|
|
588
622
|
case Events.CONTACT_LANGUAGE_CHANGED:
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
623
|
+
event._rendered = {
|
|
624
|
+
html: renderContactLanguageChangedEvent(event),
|
|
625
|
+
type: MessageType.Inline
|
|
592
626
|
};
|
|
593
627
|
break;
|
|
594
628
|
case Events.CONTACT_NAME_CHANGED:
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
629
|
+
event._rendered = {
|
|
630
|
+
html: renderNameChanged(event),
|
|
631
|
+
type: MessageType.Inline
|
|
598
632
|
};
|
|
599
633
|
break;
|
|
600
634
|
case Events.CONTACT_STATUS_CHANGED:
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
635
|
+
event._rendered = {
|
|
636
|
+
html: renderContactStatusChangedEvent(event),
|
|
637
|
+
type: MessageType.Inline
|
|
604
638
|
};
|
|
605
639
|
break;
|
|
606
640
|
case Events.CONTACT_URNS_CHANGED:
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
641
|
+
event._rendered = {
|
|
642
|
+
html: renderContactURNsChanged(event),
|
|
643
|
+
type: MessageType.Inline
|
|
610
644
|
};
|
|
611
645
|
break;
|
|
612
646
|
case Events.OPTIN_REQUESTED:
|
|
613
647
|
case Events.OPTIN_STARTED:
|
|
614
648
|
case Events.OPTIN_STOPPED:
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
649
|
+
event._rendered = {
|
|
650
|
+
html: renderOptInEvent(event),
|
|
651
|
+
type: MessageType.Inline
|
|
618
652
|
};
|
|
619
653
|
break;
|
|
620
654
|
case Events.RUN_STARTED:
|
|
621
655
|
case Events.RUN_ENDED:
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
656
|
+
event._rendered = {
|
|
657
|
+
html: renderRunEvent(event),
|
|
658
|
+
type: MessageType.Inline
|
|
625
659
|
};
|
|
626
660
|
break;
|
|
627
661
|
case Events.TICKET_ASSIGNEE_CHANGED:
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
662
|
+
event._rendered = {
|
|
663
|
+
html: renderTicketAssigneeChanged(event),
|
|
664
|
+
type: MessageType.Inline
|
|
631
665
|
};
|
|
632
666
|
break;
|
|
633
667
|
case Events.TICKET_CLOSED:
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
668
|
+
event._rendered = {
|
|
669
|
+
html: renderTicketAction(event, 'closed'),
|
|
670
|
+
type: MessageType.Inline
|
|
637
671
|
};
|
|
638
672
|
break;
|
|
639
673
|
case Events.TICKET_OPENED:
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
674
|
+
event._rendered = {
|
|
675
|
+
html: renderTicketAction(event, 'opened'),
|
|
676
|
+
type: MessageType.Inline
|
|
677
|
+
};
|
|
678
|
+
break;
|
|
679
|
+
case Events.TICKET_NOTE_ADDED:
|
|
680
|
+
event._rendered = {
|
|
681
|
+
html: renderTicketAction(event, 'noted'),
|
|
682
|
+
type: MessageType.Inline
|
|
643
683
|
};
|
|
644
684
|
break;
|
|
645
685
|
case Events.TICKET_REOPENED:
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
686
|
+
event._rendered = {
|
|
687
|
+
html: renderTicketAction(event, 'reopened'),
|
|
688
|
+
type: MessageType.Inline
|
|
649
689
|
};
|
|
650
690
|
break;
|
|
651
691
|
case Events.TICKET_TOPIC_CHANGED:
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
text: html `<div>
|
|
692
|
+
event._rendered = {
|
|
693
|
+
html: html `<div>
|
|
655
694
|
Topic changed to
|
|
656
695
|
<strong>${event.topic.name}</strong>
|
|
657
|
-
</div
|
|
696
|
+
</div>`,
|
|
697
|
+
type: MessageType.Inline
|
|
658
698
|
};
|
|
659
699
|
break;
|
|
660
700
|
case Events.CHANNEL_EVENT: // deprecated
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
701
|
+
event._rendered = {
|
|
702
|
+
html: renderChannelEvent(event),
|
|
703
|
+
type: MessageType.Inline
|
|
664
704
|
};
|
|
665
705
|
break;
|
|
666
706
|
default:
|
|
667
707
|
console.error('Unknown event type', event);
|
|
668
708
|
}
|
|
669
|
-
if (message) {
|
|
670
|
-
message.id = event.uuid;
|
|
671
|
-
message.date = new Date(event.created_on);
|
|
672
|
-
}
|
|
673
|
-
return message;
|
|
674
|
-
}
|
|
675
|
-
getUserForEvent(event) {
|
|
676
|
-
if (event.type === 'msg_received') {
|
|
677
|
-
return {
|
|
678
|
-
name: this.currentContact.name
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
else if (event._user) {
|
|
682
|
-
return event._user;
|
|
683
|
-
}
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
|
-
createChatForMessageEvent(msgEvent) {
|
|
687
|
-
return {
|
|
688
|
-
id: msgEvent.uuid,
|
|
689
|
-
type: msgEvent.type === 'msg_received' ? 'msg_in' : 'msg_out',
|
|
690
|
-
user: this.getUserForEvent(msgEvent),
|
|
691
|
-
date: new Date(msgEvent.created_on),
|
|
692
|
-
attachments: msgEvent.msg.attachments,
|
|
693
|
-
text: msgEvent.msg.text,
|
|
694
|
-
sendError: msgEvent._status &&
|
|
695
|
-
(msgEvent._status.status === 'errored' ||
|
|
696
|
-
msgEvent._status.status === 'failed'),
|
|
697
|
-
popup: html `<div
|
|
698
|
-
style="display: flex; flex-direction: row; align-items:center; justify-content: space-between;font-size:0.9em;line-height:1em;min-width:10em"
|
|
699
|
-
>
|
|
700
|
-
<div style="justify-content:left;text-align:left">
|
|
701
|
-
<temba-date
|
|
702
|
-
value=${msgEvent.created_on}
|
|
703
|
-
display="duration"
|
|
704
|
-
></temba-date>
|
|
705
|
-
|
|
706
|
-
${msgEvent.optin
|
|
707
|
-
? html `<div style="font-size:0.9em;color:#aaa">
|
|
708
|
-
${msgEvent.optin.name}
|
|
709
|
-
</div>`
|
|
710
|
-
: null}
|
|
711
|
-
</div>
|
|
712
|
-
${msgEvent._logs_url
|
|
713
|
-
? html `<a style="margin-left:0.5em" href="${msgEvent._logs_url}"
|
|
714
|
-
><temba-icon name="log"></temba-icon
|
|
715
|
-
></a>`
|
|
716
|
-
: null}
|
|
717
|
-
</div> `
|
|
718
|
-
};
|
|
719
709
|
}
|
|
720
710
|
createMessages(page) {
|
|
721
711
|
if (page.events) {
|
|
722
|
-
|
|
712
|
+
const messages = [];
|
|
723
713
|
page.events.forEach((event) => {
|
|
724
|
-
|
|
725
|
-
if (
|
|
726
|
-
this.
|
|
727
|
-
|
|
728
|
-
if (event.type === 'ticket_note_added') {
|
|
729
|
-
const ticketEvent = event;
|
|
730
|
-
messages.push({
|
|
731
|
-
type: MessageType.Note,
|
|
732
|
-
id: event.created_on + event.type,
|
|
733
|
-
user: this.getUserForEvent(ticketEvent),
|
|
734
|
-
date: new Date(ticketEvent.created_on),
|
|
735
|
-
text: ticketEvent.note
|
|
736
|
-
});
|
|
714
|
+
// track the UUID of the newest event for polling
|
|
715
|
+
if (!this.afterUUID ||
|
|
716
|
+
event.uuid.toLowerCase() > this.afterUUID.toLowerCase()) {
|
|
717
|
+
this.afterUUID = event.uuid;
|
|
737
718
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
messages.push({
|
|
742
|
-
type: MessageType.Note,
|
|
743
|
-
id: event.created_on + event.type + '_note',
|
|
744
|
-
user: this.getUserForEvent(ticketEvent),
|
|
745
|
-
date: new Date(ticketEvent.created_on),
|
|
746
|
-
text: ticketEvent.note
|
|
747
|
-
});
|
|
748
|
-
// but the opening of the ticket is a normal event
|
|
749
|
-
messages.push(this.getEventMessage(event));
|
|
750
|
-
}
|
|
751
|
-
else if (event.type === 'msg_created' ||
|
|
719
|
+
// convert to dates
|
|
720
|
+
event.created_on = new Date(event.created_on);
|
|
721
|
+
if (event.type === 'msg_created' ||
|
|
752
722
|
event.type === 'msg_received' ||
|
|
753
723
|
event.type === 'ivr_created') {
|
|
754
|
-
|
|
755
|
-
messages.push(this.createChatForMessageEvent(msgEvent));
|
|
724
|
+
messages.push(event);
|
|
756
725
|
}
|
|
757
726
|
else {
|
|
758
|
-
|
|
759
|
-
if (
|
|
760
|
-
messages.push(
|
|
727
|
+
this.prerender(event);
|
|
728
|
+
if (event._rendered) {
|
|
729
|
+
messages.push(event);
|
|
761
730
|
}
|
|
762
731
|
}
|
|
763
732
|
});
|
|
764
733
|
// remove any messages we don't recognize
|
|
765
|
-
|
|
766
|
-
return messages;
|
|
734
|
+
return messages.filter((msg) => !!msg);
|
|
767
735
|
}
|
|
768
736
|
return [];
|
|
769
737
|
}
|
|
@@ -774,26 +742,26 @@ export class ContactChat extends ContactStoreElement {
|
|
|
774
742
|
return;
|
|
775
743
|
}
|
|
776
744
|
const chat = this.chat;
|
|
777
|
-
|
|
778
|
-
if (this.currentContact && this.newestEventTime) {
|
|
745
|
+
if (this.currentContact && this.afterUUID) {
|
|
779
746
|
this.polling = true;
|
|
747
|
+
this.lastFetchTime = Date.now();
|
|
780
748
|
const endpoint = this.getEndpoint();
|
|
781
749
|
if (!endpoint) {
|
|
782
750
|
return;
|
|
783
751
|
}
|
|
784
752
|
const fetchContact = this.currentContact.uuid;
|
|
785
|
-
fetchContactHistory(
|
|
753
|
+
fetchContactHistory(endpoint, (_a = this.currentTicket) === null || _a === void 0 ? void 0 : _a.uuid, null, this.afterUUID).then((page) => {
|
|
754
|
+
const messages = this.createMessages(page);
|
|
755
|
+
messages.reverse();
|
|
786
756
|
if (fetchContact === this.currentContact.uuid) {
|
|
787
|
-
|
|
788
|
-
const messages = this.createMessages(page);
|
|
789
|
-
if (messages.length === 0) {
|
|
790
|
-
contactChat.blockFetching = true;
|
|
791
|
-
}
|
|
792
|
-
messages.reverse();
|
|
757
|
+
const hasNewEvents = messages.length > 0;
|
|
793
758
|
chat.addMessages(messages, null, true);
|
|
759
|
+
this.polling = false;
|
|
760
|
+
this.scheduleRefresh(hasNewEvents);
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
this.polling = false;
|
|
794
764
|
}
|
|
795
|
-
this.polling = false;
|
|
796
|
-
this.scheduleRefresh();
|
|
797
765
|
});
|
|
798
766
|
}
|
|
799
767
|
}
|
|
@@ -810,13 +778,31 @@ export class ContactChat extends ContactStoreElement {
|
|
|
810
778
|
if (!endpoint) {
|
|
811
779
|
return;
|
|
812
780
|
}
|
|
813
|
-
|
|
814
|
-
|
|
781
|
+
// initialize anchor UUID if not set (first fetch)
|
|
782
|
+
if (!this.beforeUUID && !this.afterUUID) {
|
|
783
|
+
// generate a UUID v7 for current time as the anchor
|
|
784
|
+
const anchorUUID = generateUUIDv7();
|
|
785
|
+
this.beforeUUID = anchorUUID;
|
|
786
|
+
this.afterUUID = anchorUUID;
|
|
787
|
+
}
|
|
788
|
+
fetchContactHistory(endpoint, (_a = this.currentTicket) === null || _a === void 0 ? void 0 : _a.uuid, this.beforeUUID, null).then((page) => {
|
|
815
789
|
const messages = this.createMessages(page);
|
|
816
790
|
messages.reverse();
|
|
817
791
|
if (messages.length === 0) {
|
|
818
792
|
contactChat.blockFetching = true;
|
|
819
793
|
}
|
|
794
|
+
else if (page.next) {
|
|
795
|
+
// update beforeUUID for next fetch of older messages
|
|
796
|
+
this.beforeUUID = page.next;
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
// no more history, mark end and show oldest event date
|
|
800
|
+
contactChat.blockFetching = true;
|
|
801
|
+
if (page.events && page.events.length > 0) {
|
|
802
|
+
const oldestEvent = page.events[page.events.length - 1];
|
|
803
|
+
chat.setEndOfHistory(new Date(oldestEvent.created_on));
|
|
804
|
+
}
|
|
805
|
+
}
|
|
820
806
|
chat.addMessages(messages);
|
|
821
807
|
this.scheduleRefresh();
|
|
822
808
|
});
|
|
@@ -921,15 +907,13 @@ export class ContactChat extends ContactStoreElement {
|
|
|
921
907
|
refreshTicket() {
|
|
922
908
|
if (this.currentTicket) {
|
|
923
909
|
fetchResults(`/api/v2/tickets.json?uuid=${this.currentTicket.uuid}`).then((values) => {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}
|
|
932
|
-
});
|
|
910
|
+
if (values.length > 0) {
|
|
911
|
+
this.fireCustomEvent(CustomEventType.TicketUpdated, {
|
|
912
|
+
ticket: values[0],
|
|
913
|
+
previous: this.currentTicket
|
|
914
|
+
});
|
|
915
|
+
this.currentTicket = values[0];
|
|
916
|
+
}
|
|
933
917
|
});
|
|
934
918
|
}
|
|
935
919
|
}
|
|
@@ -970,6 +954,7 @@ export class ContactChat extends ContactStoreElement {
|
|
|
970
954
|
@temba-fetch-complete=${this.fetchComplete}
|
|
971
955
|
avatar=${this.avatar}
|
|
972
956
|
agent
|
|
957
|
+
?hasFooter=${inFlow}
|
|
973
958
|
>
|
|
974
959
|
${inFlow
|
|
975
960
|
? html `
|
|
@@ -1099,37 +1084,31 @@ export const fetchContact = (endpoint) => {
|
|
|
1099
1084
|
});
|
|
1100
1085
|
});
|
|
1101
1086
|
};
|
|
1102
|
-
export const fetchContactHistory = (
|
|
1103
|
-
if (reset) {
|
|
1104
|
-
pendingRequests.forEach((controller) => {
|
|
1105
|
-
controller.abort();
|
|
1106
|
-
});
|
|
1107
|
-
pendingRequests = [];
|
|
1108
|
-
}
|
|
1087
|
+
export const fetchContactHistory = (endpoint, ticket = undefined, before = undefined, after = undefined) => {
|
|
1109
1088
|
return new Promise((resolve) => {
|
|
1110
1089
|
const controller = new AbortController();
|
|
1111
1090
|
pendingRequests.push(controller);
|
|
1112
1091
|
let url = endpoint;
|
|
1092
|
+
const params = [];
|
|
1113
1093
|
if (before) {
|
|
1114
|
-
|
|
1094
|
+
params.push(`before=${before}`);
|
|
1115
1095
|
}
|
|
1116
1096
|
if (after) {
|
|
1117
|
-
|
|
1097
|
+
params.push(`after=${after}`);
|
|
1118
1098
|
}
|
|
1119
1099
|
if (ticket) {
|
|
1120
|
-
|
|
1100
|
+
params.push(`ticket=${ticket}`);
|
|
1101
|
+
}
|
|
1102
|
+
if (params.length > 0) {
|
|
1103
|
+
url += (url.includes('?') ? '&' : '?') + params.join('&');
|
|
1121
1104
|
}
|
|
1122
|
-
const store = document.querySelector('temba-store');
|
|
1123
1105
|
getUrl(url, controller)
|
|
1124
1106
|
.then((response) => {
|
|
1125
1107
|
// on success, remove our abort controller
|
|
1126
1108
|
pendingRequests = pendingRequests.filter((controller) => {
|
|
1127
1109
|
return response.controller === controller;
|
|
1128
1110
|
});
|
|
1129
|
-
|
|
1130
|
-
store.resolveUsers(page.events, ['created_by']).then(() => {
|
|
1131
|
-
resolve(page);
|
|
1132
|
-
});
|
|
1111
|
+
resolve(response.json);
|
|
1133
1112
|
})
|
|
1134
1113
|
.catch(() => {
|
|
1135
1114
|
// canceled
|