@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.
- package/CHANGELOG.md +9 -0
- package/demo/index.html +1 -1
- package/dist/temba-components.js +760 -1189
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/chat/Chat.js +714 -0
- package/out-tsc/src/chat/Chat.js.map +1 -0
- package/out-tsc/src/completion/helpers.js +1 -29
- package/out-tsc/src/completion/helpers.js.map +1 -1
- package/out-tsc/src/compose/Compose.js +6 -2
- package/out-tsc/src/compose/Compose.js.map +1 -1
- package/out-tsc/src/contacts/ContactChat.js +518 -54
- package/out-tsc/src/contacts/ContactChat.js.map +1 -1
- package/out-tsc/src/contacts/events.js +1 -998
- package/out-tsc/src/contacts/events.js.map +1 -1
- package/out-tsc/src/lightbox/Lightbox.js +4 -0
- package/out-tsc/src/lightbox/Lightbox.js.map +1 -1
- package/out-tsc/src/list/TembaMenu.js +0 -1
- package/out-tsc/src/list/TembaMenu.js.map +1 -1
- package/out-tsc/src/markdown.js +33 -0
- package/out-tsc/src/markdown.js.map +1 -0
- package/out-tsc/src/select/Select.js +6 -1
- package/out-tsc/src/select/Select.js.map +1 -1
- package/out-tsc/src/textinput/TextInput.js +1 -1
- package/out-tsc/src/textinput/TextInput.js.map +1 -1
- package/out-tsc/src/thumbnail/Thumbnail.js +128 -81
- package/out-tsc/src/thumbnail/Thumbnail.js.map +1 -1
- package/out-tsc/src/utils/index.js +9 -11
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/src/webchat/WebChat.js +109 -358
- package/out-tsc/src/webchat/WebChat.js.map +1 -1
- package/out-tsc/src/webchat/index.js +17 -0
- package/out-tsc/src/webchat/index.js.map +1 -1
- package/out-tsc/temba-modules.js +2 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/temba-webchat.js +2 -0
- package/out-tsc/temba-webchat.js.map +1 -1
- package/out-tsc/test/temba-contact-chat.test.js +1 -0
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-lightbox.test.js +4 -4
- package/out-tsc/test/temba-lightbox.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/contacts/compose-attachments-no-text-failure.png +0 -0
- package/screenshots/truth/contacts/compose-attachments-no-text-success.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-failure-attachments.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-failure-generic.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-failure-text-and-attachments.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-failure-text.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-success.png +0 -0
- package/screenshots/truth/contacts/compose-text-no-attachments-failure.png +0 -0
- package/screenshots/truth/contacts/compose-text-no-attachments-success.png +0 -0
- package/screenshots/truth/contacts/contact-active-default.png +0 -0
- package/screenshots/truth/contacts/contact-active-show-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-archived-hide-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-blocked-hide-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-stopped-hide-chatbox.png +0 -0
- package/screenshots/truth/lightbox/img-zoomed.png +0 -0
- package/screenshots/truth/lightbox/img.png +0 -0
- package/src/chat/Chat.ts +791 -0
- package/src/completion/helpers.ts +2 -40
- package/src/compose/Compose.ts +6 -2
- package/src/contacts/ContactChat.ts +609 -59
- package/src/contacts/events.ts +1 -1068
- package/src/lightbox/Lightbox.ts +5 -0
- package/src/list/TembaMenu.ts +0 -1
- package/src/markdown.ts +41 -0
- package/src/select/Select.ts +5 -1
- package/src/textinput/TextInput.ts +1 -1
- package/src/thumbnail/Thumbnail.ts +130 -81
- package/src/utils/index.ts +12 -13
- package/src/webchat/WebChat.ts +196 -413
- package/src/webchat/index.ts +23 -1
- package/static/css/temba-components.css +2 -0
- package/temba-modules.ts +2 -2
- package/temba-webchat.ts +2 -0
- package/test/temba-contact-chat.test.ts +1 -0
- package/test/temba-lightbox.test.ts +4 -4
- package/test-assets/contacts/history.json +1 -56
- package/out-tsc/src/contacts/ContactHistory.js +0 -691
- package/out-tsc/src/contacts/ContactHistory.js.map +0 -1
- package/out-tsc/test/temba-contact-history.test.js +0 -69
- package/out-tsc/test/temba-contact-history.test.js.map +0 -1
- package/src/contacts/ContactHistory.ts +0 -875
- package/test/temba-contact-history.test.ts +0 -107
|
@@ -1,691 +0,0 @@
|
|
|
1
|
-
import { __decorate } from "tslib";
|
|
2
|
-
import { css } from 'lit';
|
|
3
|
-
import { property } from 'lit/decorators.js';
|
|
4
|
-
import { html } from 'lit-html';
|
|
5
|
-
import { CustomEventType } from '../interfaces';
|
|
6
|
-
import { RapidElement } from '../RapidElement';
|
|
7
|
-
import { getAssets, getClasses, postJSON, throttle } from '../utils';
|
|
8
|
-
import { Events, getEventGroupType, getEventStyles, renderAirtimeTransferredEvent, renderCallStartedEvent, renderCampaignFiredEvent, renderChannelEvent, renderContactGroupsEvent, renderContactLanguageChangedEvent, renderContactURNsChanged, renderEmailSent, renderErrorMessage, renderFlowEvent, renderLabelsAdded, renderMsgEvent, renderNameChanged, renderNoteCreated, renderOptinRequested, renderResultEvent, renderTicketAction, renderTicketAssigned, renderUpdateEvent, renderWebhookEvent } from './events';
|
|
9
|
-
import { fetchContactHistory, MAX_CHAT_REFRESH, MIN_CHAT_REFRESH, SCROLL_THRESHOLD } from './helpers';
|
|
10
|
-
// when images load, make sure we are on the bottom of the scroll window if necessary
|
|
11
|
-
export const loadHandler = function (event) {
|
|
12
|
-
const target = event.target;
|
|
13
|
-
const events = this.host.getEventsPane();
|
|
14
|
-
if (target.tagName == 'IMG') {
|
|
15
|
-
if (!this.host.showMessageAlert) {
|
|
16
|
-
if (events.scrollTop > target.offsetTop - 1000 &&
|
|
17
|
-
target.offsetTop > events.scrollHeight - 500) {
|
|
18
|
-
this.host.scrollToBottom();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
export class ContactHistory extends RapidElement {
|
|
24
|
-
constructor() {
|
|
25
|
-
super();
|
|
26
|
-
this.eventGroups = [];
|
|
27
|
-
this.refreshing = false;
|
|
28
|
-
this.fetching = false;
|
|
29
|
-
this.complete = false;
|
|
30
|
-
this.debug = false;
|
|
31
|
-
this.showMessageAlert = false;
|
|
32
|
-
this.ticket = null;
|
|
33
|
-
this.endDate = null;
|
|
34
|
-
this.tickets = null;
|
|
35
|
-
this.ticketEvents = {};
|
|
36
|
-
this.lastHeight = 0;
|
|
37
|
-
this.refreshTimeout = null;
|
|
38
|
-
this.empty = false;
|
|
39
|
-
}
|
|
40
|
-
connectedCallback() {
|
|
41
|
-
super.connectedCallback();
|
|
42
|
-
this.shadowRoot.addEventListener('load', loadHandler, true);
|
|
43
|
-
this.store = document.querySelector('temba-store');
|
|
44
|
-
}
|
|
45
|
-
disconnectedCallback() {
|
|
46
|
-
super.disconnectedCallback();
|
|
47
|
-
this.shadowRoot.removeEventListener('load', loadHandler, true);
|
|
48
|
-
}
|
|
49
|
-
getTicketForEvent(event) {
|
|
50
|
-
return this.getTicket(event.ticket.uuid);
|
|
51
|
-
}
|
|
52
|
-
getTicket(uuid) {
|
|
53
|
-
return (this.tickets || []).find((ticket) => ticket.uuid === uuid);
|
|
54
|
-
}
|
|
55
|
-
static get styles() {
|
|
56
|
-
return css `
|
|
57
|
-
${getEventStyles()}
|
|
58
|
-
|
|
59
|
-
.wrapper {
|
|
60
|
-
border: 0px solid green;
|
|
61
|
-
display: flex;
|
|
62
|
-
flex-direction: column;
|
|
63
|
-
align-items: items-stretch;
|
|
64
|
-
flex-grow: 1;
|
|
65
|
-
min-height: 0;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.events {
|
|
69
|
-
overflow-y: scroll;
|
|
70
|
-
overflow-x: hidden;
|
|
71
|
-
background: #fff;
|
|
72
|
-
display: flex;
|
|
73
|
-
flex-direction: column;
|
|
74
|
-
flex-grow: 1;
|
|
75
|
-
min-height: 0;
|
|
76
|
-
padding-top: 3em;
|
|
77
|
-
padding-bottom: 1em;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
temba-loading {
|
|
81
|
-
align-self: center;
|
|
82
|
-
margin-top: 0.025em;
|
|
83
|
-
position: absolute;
|
|
84
|
-
z-index: 250;
|
|
85
|
-
padding-top: 1em;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.new-messages-container {
|
|
89
|
-
display: flex;
|
|
90
|
-
z-index: 1;
|
|
91
|
-
background: pink;
|
|
92
|
-
margin-bottom: 0px;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.new-messages {
|
|
96
|
-
pointer-events: none;
|
|
97
|
-
margin: 0 auto;
|
|
98
|
-
margin-top: 0em;
|
|
99
|
-
margin-bottom: -2.5em;
|
|
100
|
-
padding: 0.25em 1em;
|
|
101
|
-
border-radius: var(--curvature);
|
|
102
|
-
background: var(--color-primary-dark);
|
|
103
|
-
color: var(--color-text-light);
|
|
104
|
-
opacity: 0;
|
|
105
|
-
cursor: pointer;
|
|
106
|
-
transition: all var(--transition-speed) ease-in-out;
|
|
107
|
-
box-shadow: rgb(0 0 0 / 15%) 0px 3px 3px 0px;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.new-messages.expanded {
|
|
111
|
-
margin-top: -2.5em;
|
|
112
|
-
margin-bottom: 0.5em;
|
|
113
|
-
pointer-events: auto;
|
|
114
|
-
opacity: 1;
|
|
115
|
-
pointer: cursor;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.scroll-title {
|
|
119
|
-
display: flex;
|
|
120
|
-
flex-direction: column;
|
|
121
|
-
z-index: 2;
|
|
122
|
-
border-top-left-radius: var(--curvature);
|
|
123
|
-
overflow: hidden;
|
|
124
|
-
box-shadow: 0px 3px 3px 0px rgba(0, 0, 0, 0.15);
|
|
125
|
-
background: rgb(240, 240, 240);
|
|
126
|
-
padding: 1em 1.2em;
|
|
127
|
-
font-size: 1.2em;
|
|
128
|
-
font-weight: 400;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.attachment img {
|
|
132
|
-
cursor: pointer;
|
|
133
|
-
}
|
|
134
|
-
`;
|
|
135
|
-
}
|
|
136
|
-
firstUpdated(changedProperties) {
|
|
137
|
-
super.firstUpdated(changedProperties);
|
|
138
|
-
this.handleClose = this.handleClose.bind(this);
|
|
139
|
-
}
|
|
140
|
-
updated(changedProperties) {
|
|
141
|
-
super.updated(changedProperties);
|
|
142
|
-
// fire an event if we get a new event
|
|
143
|
-
if (changedProperties.has('mostRecentEvent') &&
|
|
144
|
-
changedProperties.get('mostRecentEvent') &&
|
|
145
|
-
this.mostRecentEvent) {
|
|
146
|
-
this.fireCustomEvent(CustomEventType.Refreshed);
|
|
147
|
-
}
|
|
148
|
-
if (changedProperties.has('endDate')) {
|
|
149
|
-
if (this.refreshTimeout && this.endDate) {
|
|
150
|
-
window.clearTimeout(this.refreshTimeout);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
// if we don't have an endpoint infer one
|
|
154
|
-
if (changedProperties.has('uuid')) {
|
|
155
|
-
if (this.uuid == null) {
|
|
156
|
-
this.reset();
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
const endpoint = `/contact/history/${this.uuid}/?_format=json`;
|
|
160
|
-
if (this.endpoint !== endpoint) {
|
|
161
|
-
this.reset();
|
|
162
|
-
if (this.endDate) {
|
|
163
|
-
const before = new Date(this.endDate);
|
|
164
|
-
this.nextBefore = before.getTime() * 1000 + 1000;
|
|
165
|
-
}
|
|
166
|
-
this.endpoint = endpoint;
|
|
167
|
-
this.refreshTickets();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (changedProperties.has('ticket')) {
|
|
172
|
-
this.endpoint = null;
|
|
173
|
-
this.requestUpdate('uuid');
|
|
174
|
-
}
|
|
175
|
-
if (changedProperties.has('refreshing') &&
|
|
176
|
-
this.refreshing &&
|
|
177
|
-
this.endpoint &&
|
|
178
|
-
!this.endDate) {
|
|
179
|
-
const after = (this.getLastEventTime() - 1) * 1000;
|
|
180
|
-
let forceOpen = false;
|
|
181
|
-
fetchContactHistory(false, this.endpoint, this.ticket, null, after)
|
|
182
|
-
.then((results) => {
|
|
183
|
-
if (results.events && results.events.length > 0) {
|
|
184
|
-
this.updateMostRecent(results.events[0]);
|
|
185
|
-
}
|
|
186
|
-
// keep track of any ticket events
|
|
187
|
-
results.events.forEach((event) => {
|
|
188
|
-
if (event.type === Events.TICKET_OPENED) {
|
|
189
|
-
const ticketEvent = event;
|
|
190
|
-
this.ticketEvents[ticketEvent.ticket.uuid] = ticketEvent;
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
const fetchedEvents = results.events.reverse();
|
|
194
|
-
// dedupe any events we get from the server
|
|
195
|
-
// TODO: perhaps make this a little less crazy
|
|
196
|
-
let removed = 0;
|
|
197
|
-
this.eventGroups.forEach((g) => {
|
|
198
|
-
const before = g.events.length;
|
|
199
|
-
g.events = g.events.filter((prev) => !fetchedEvents.find((fetched) => {
|
|
200
|
-
return (prev.created_on == fetched.created_on &&
|
|
201
|
-
prev.type === fetched.type);
|
|
202
|
-
}));
|
|
203
|
-
removed += before - g.events.length;
|
|
204
|
-
});
|
|
205
|
-
this.lastRefreshAdded = fetchedEvents.length - removed;
|
|
206
|
-
// reflow our most recent event group in case it merges with our new groups
|
|
207
|
-
const previousGroups = [...this.eventGroups];
|
|
208
|
-
if (this.eventGroups.length > 0) {
|
|
209
|
-
const sliced = previousGroups.splice(previousGroups.length - 1, 1)[0];
|
|
210
|
-
forceOpen = sliced.open;
|
|
211
|
-
if (sliced.events.length > 0) {
|
|
212
|
-
fetchedEvents.splice(0, 0, ...sliced.events);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
const grouped = this.getEventGroups(fetchedEvents);
|
|
216
|
-
if (grouped.length) {
|
|
217
|
-
if (forceOpen) {
|
|
218
|
-
grouped[grouped.length - 1].open = forceOpen;
|
|
219
|
-
}
|
|
220
|
-
this.eventGroups = [...previousGroups, ...grouped].filter((group) => group.events.length > 0);
|
|
221
|
-
}
|
|
222
|
-
this.refreshing = false;
|
|
223
|
-
this.scheduleRefresh();
|
|
224
|
-
})
|
|
225
|
-
.catch(() => {
|
|
226
|
-
this.refreshing = false;
|
|
227
|
-
this.scheduleRefresh();
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
if (changedProperties.has('fetching') && this.fetching) {
|
|
231
|
-
if (!this.nextBefore) {
|
|
232
|
-
this.nextBefore = new Date().getTime() * 1000 - 1000;
|
|
233
|
-
}
|
|
234
|
-
this.httpComplete = fetchContactHistory(this.empty, this.endpoint, this.ticket, this.nextBefore, this.nextAfter).then((results) => {
|
|
235
|
-
// see if we have a new event
|
|
236
|
-
if (results.events && results.events.length > 0) {
|
|
237
|
-
this.updateMostRecent(results.events[0]);
|
|
238
|
-
// keep track of any ticket events
|
|
239
|
-
results.events.forEach((event) => {
|
|
240
|
-
if (event.type === Events.TICKET_OPENED) {
|
|
241
|
-
const ticketEvent = event;
|
|
242
|
-
this.ticketEvents[ticketEvent.ticket.uuid] = ticketEvent;
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
let forceOpen = false;
|
|
247
|
-
const fetchedEvents = results.events ? results.events.reverse() : [];
|
|
248
|
-
// reflow our last event group in case it merges with our new groups
|
|
249
|
-
if (this.eventGroups.length > 0) {
|
|
250
|
-
const sliced = this.eventGroups.splice(0, 1)[0];
|
|
251
|
-
forceOpen = sliced.open;
|
|
252
|
-
fetchedEvents.push(...sliced.events);
|
|
253
|
-
}
|
|
254
|
-
const grouped = this.getEventGroups(fetchedEvents);
|
|
255
|
-
if (grouped.length) {
|
|
256
|
-
if (forceOpen) {
|
|
257
|
-
grouped[grouped.length - 1].open = forceOpen;
|
|
258
|
-
}
|
|
259
|
-
this.eventGroups = [...grouped, ...this.eventGroups];
|
|
260
|
-
}
|
|
261
|
-
if (results.next_before === this.nextBefore) {
|
|
262
|
-
this.complete = true;
|
|
263
|
-
}
|
|
264
|
-
this.nextBefore = results.next_before;
|
|
265
|
-
this.nextAfter = results.next_after;
|
|
266
|
-
this.fetching = false;
|
|
267
|
-
this.empty = false;
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
if (changedProperties.has('refreshing') && !this.refreshing) {
|
|
271
|
-
if (this.lastRefreshAdded > 0) {
|
|
272
|
-
const events = this.getEventsPane();
|
|
273
|
-
// if we are near the bottom, push us to the bottom to show new stuff
|
|
274
|
-
if (this.lastHeight > 0) {
|
|
275
|
-
const addedHeight = events.scrollHeight - this.lastHeight;
|
|
276
|
-
const distanceFromBottom = events.scrollHeight -
|
|
277
|
-
events.scrollTop -
|
|
278
|
-
addedHeight -
|
|
279
|
-
events.clientHeight;
|
|
280
|
-
if (distanceFromBottom < 500) {
|
|
281
|
-
this.scrollToBottom();
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
this.showMessageAlert = true;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (this.eventGroups.length > 0) {
|
|
288
|
-
this.lastHeight = events.scrollHeight;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
if (changedProperties.has('fetching') &&
|
|
293
|
-
!this.fetching &&
|
|
294
|
-
changedProperties.get('fetching') !== undefined) {
|
|
295
|
-
const events = this.getEventsPane();
|
|
296
|
-
if (this.lastHeight && events.scrollHeight > this.lastHeight) {
|
|
297
|
-
const scrollTop = events.scrollTop + events.scrollHeight - this.lastHeight;
|
|
298
|
-
events.scrollTop = scrollTop;
|
|
299
|
-
}
|
|
300
|
-
// scroll to the bottom if it's our first fetch
|
|
301
|
-
if (!this.lastHeight) {
|
|
302
|
-
this.scrollToBottom();
|
|
303
|
-
}
|
|
304
|
-
// don't record our scroll height until we have history
|
|
305
|
-
if (this.eventGroups.length > 0) {
|
|
306
|
-
this.lastHeight = events.scrollHeight;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
if (changedProperties.has('endpoint') && this.endpoint) {
|
|
310
|
-
this.fetching = true;
|
|
311
|
-
this.empty = true;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
refreshTickets() {
|
|
315
|
-
if (this.ticket) {
|
|
316
|
-
let url = `/api/v2/tickets.json?contact=${this.uuid}`;
|
|
317
|
-
if (this.ticket) {
|
|
318
|
-
url = `${url}&ticket=${this.ticket}`;
|
|
319
|
-
}
|
|
320
|
-
getAssets(url).then((tickets) => {
|
|
321
|
-
this.tickets = tickets.reverse();
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
getEventsPane() {
|
|
326
|
-
return this.getDiv('.events');
|
|
327
|
-
}
|
|
328
|
-
scrollToBottom(smooth = false) {
|
|
329
|
-
const events = this.getEventsPane();
|
|
330
|
-
events.scrollTo({
|
|
331
|
-
top: events.scrollHeight,
|
|
332
|
-
behavior: smooth ? 'smooth' : 'auto'
|
|
333
|
-
});
|
|
334
|
-
this.showMessageAlert = false;
|
|
335
|
-
window.setTimeout(() => {
|
|
336
|
-
events.scrollTo({
|
|
337
|
-
top: events.scrollHeight,
|
|
338
|
-
behavior: smooth ? 'smooth' : 'auto'
|
|
339
|
-
});
|
|
340
|
-
}, 0);
|
|
341
|
-
}
|
|
342
|
-
refresh() {
|
|
343
|
-
this.scheduleRefresh(500);
|
|
344
|
-
}
|
|
345
|
-
getEventGroups(events) {
|
|
346
|
-
const grouped = [];
|
|
347
|
-
let eventGroup = undefined;
|
|
348
|
-
for (const event of events) {
|
|
349
|
-
const currentEventGroupType = getEventGroupType(event, this.ticket);
|
|
350
|
-
// see if we need a new event group
|
|
351
|
-
if (!eventGroup || eventGroup.type !== currentEventGroupType) {
|
|
352
|
-
// we have a new type, save our last group
|
|
353
|
-
if (eventGroup) {
|
|
354
|
-
grouped.push(eventGroup);
|
|
355
|
-
}
|
|
356
|
-
eventGroup = {
|
|
357
|
-
open: false,
|
|
358
|
-
events: [event],
|
|
359
|
-
type: currentEventGroupType
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
// our event matches the current group, stuff it in there
|
|
364
|
-
eventGroup.events.push(event);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
if (eventGroup && eventGroup.events.length > 0) {
|
|
368
|
-
grouped.push(eventGroup);
|
|
369
|
-
}
|
|
370
|
-
return grouped;
|
|
371
|
-
}
|
|
372
|
-
scheduleRefresh(wait = -1) {
|
|
373
|
-
if (this.endDate) {
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
let refreshWait = wait;
|
|
377
|
-
if (wait === -1) {
|
|
378
|
-
const lastEventTime = this.getLastEventTime();
|
|
379
|
-
refreshWait = Math.max(Math.min((new Date().getTime() - lastEventTime) / 2, MAX_CHAT_REFRESH), MIN_CHAT_REFRESH);
|
|
380
|
-
}
|
|
381
|
-
// cancel any outstanding timeout
|
|
382
|
-
if (wait > -1 && this.refreshTimeout) {
|
|
383
|
-
window.clearTimeout(this.refreshTimeout);
|
|
384
|
-
}
|
|
385
|
-
this.refreshTimeout = window.setTimeout(() => {
|
|
386
|
-
if (this.refreshing) {
|
|
387
|
-
this.scheduleRefresh();
|
|
388
|
-
this.refreshing = false;
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
this.refreshing = true;
|
|
392
|
-
}
|
|
393
|
-
}, refreshWait);
|
|
394
|
-
}
|
|
395
|
-
reset() {
|
|
396
|
-
this.endpoint = null;
|
|
397
|
-
this.tickets = null;
|
|
398
|
-
this.ticketEvents = {};
|
|
399
|
-
this.eventGroups = [];
|
|
400
|
-
this.fetching = false;
|
|
401
|
-
this.complete = false;
|
|
402
|
-
this.nextBefore = null;
|
|
403
|
-
this.nextAfter = null;
|
|
404
|
-
this.lastHeight = 0;
|
|
405
|
-
}
|
|
406
|
-
handleEventGroupShow(event) {
|
|
407
|
-
const grouping = event.currentTarget;
|
|
408
|
-
const groupIndex = parseInt(grouping.getAttribute('data-group-index'));
|
|
409
|
-
const eventGroup = this.eventGroups[this.eventGroups.length - groupIndex - 1];
|
|
410
|
-
eventGroup.open = true;
|
|
411
|
-
this.requestUpdate('eventGroups');
|
|
412
|
-
}
|
|
413
|
-
handleEventGroupHide(event) {
|
|
414
|
-
event.preventDefault();
|
|
415
|
-
event.stopPropagation();
|
|
416
|
-
const grouping = event.currentTarget;
|
|
417
|
-
const groupIndex = parseInt(grouping.getAttribute('data-group-index'));
|
|
418
|
-
const eventGroup = this.eventGroups[this.eventGroups.length - groupIndex - 1];
|
|
419
|
-
eventGroup.open = false;
|
|
420
|
-
this.requestUpdate('eventGroups');
|
|
421
|
-
}
|
|
422
|
-
handleScroll() {
|
|
423
|
-
const events = this.getEventsPane();
|
|
424
|
-
if (events.scrollTop <= SCROLL_THRESHOLD) {
|
|
425
|
-
if (this.eventGroups.length > 0 && !this.fetching && !this.complete) {
|
|
426
|
-
this.fetching = true;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
updateMostRecent(newEvent) {
|
|
431
|
-
if (!this.mostRecentEvent ||
|
|
432
|
-
this.mostRecentEvent.type !== newEvent.type ||
|
|
433
|
-
this.mostRecentEvent.created_on !== newEvent.created_on) {
|
|
434
|
-
this.mostRecentEvent = newEvent;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
getLastEventTime() {
|
|
438
|
-
const mostRecentGroup = this.eventGroups[this.eventGroups.length - 1];
|
|
439
|
-
if (mostRecentGroup) {
|
|
440
|
-
const mostRecentEvent = mostRecentGroup.events[mostRecentGroup.events.length - 1];
|
|
441
|
-
return new Date(mostRecentEvent.created_on).getTime();
|
|
442
|
-
}
|
|
443
|
-
return 0;
|
|
444
|
-
}
|
|
445
|
-
renderEvent(event) {
|
|
446
|
-
switch (event.type) {
|
|
447
|
-
case Events.IVR_CREATED:
|
|
448
|
-
case Events.MESSAGE_CREATED:
|
|
449
|
-
case Events.MESSAGE_RECEIVED:
|
|
450
|
-
case Events.BROADCAST_CREATED:
|
|
451
|
-
if (event.created_by) {
|
|
452
|
-
event.created_by = this.store.getUser(event.created_by.email);
|
|
453
|
-
}
|
|
454
|
-
return renderMsgEvent(event);
|
|
455
|
-
case Events.FLOW_ENTERED:
|
|
456
|
-
case Events.FLOW_EXITED:
|
|
457
|
-
return renderFlowEvent(event);
|
|
458
|
-
case Events.RUN_RESULT_CHANGED:
|
|
459
|
-
return renderResultEvent(event);
|
|
460
|
-
case Events.CONTACT_FIELD_CHANGED:
|
|
461
|
-
return renderUpdateEvent(event);
|
|
462
|
-
case Events.CONTACT_NAME_CHANGED:
|
|
463
|
-
return renderNameChanged(event);
|
|
464
|
-
case Events.CONTACT_URNS_CHANGED:
|
|
465
|
-
return renderContactURNsChanged(event);
|
|
466
|
-
case Events.EMAIL_SENT:
|
|
467
|
-
return renderEmailSent(event);
|
|
468
|
-
case Events.INPUT_LABELS_ADDED:
|
|
469
|
-
return renderLabelsAdded(event);
|
|
470
|
-
case Events.TICKET_OPENED: {
|
|
471
|
-
return renderTicketAction(event, 'opened', !this.ticket);
|
|
472
|
-
}
|
|
473
|
-
case Events.TICKET_NOTE_ADDED:
|
|
474
|
-
return renderNoteCreated(event);
|
|
475
|
-
case Events.TICKET_ASSIGNED:
|
|
476
|
-
return renderTicketAssigned(event);
|
|
477
|
-
case Events.TICKET_REOPENED: {
|
|
478
|
-
return renderTicketAction(event, 'reopened', !this.ticket);
|
|
479
|
-
}
|
|
480
|
-
case Events.TICKET_CLOSED:
|
|
481
|
-
return renderTicketAction(event, 'closed', !this.ticket);
|
|
482
|
-
case Events.ERROR:
|
|
483
|
-
case Events.FAILURE:
|
|
484
|
-
return renderErrorMessage(event);
|
|
485
|
-
case Events.CONTACT_GROUPS_CHANGED:
|
|
486
|
-
return renderContactGroupsEvent(event);
|
|
487
|
-
case Events.WEBHOOK_CALLED:
|
|
488
|
-
return renderWebhookEvent(event);
|
|
489
|
-
case Events.AIRTIME_TRANSFERRED:
|
|
490
|
-
return renderAirtimeTransferredEvent(event);
|
|
491
|
-
case Events.CALL_STARTED:
|
|
492
|
-
return renderCallStartedEvent();
|
|
493
|
-
case Events.CAMPAIGN_FIRED:
|
|
494
|
-
return renderCampaignFiredEvent(event);
|
|
495
|
-
case Events.CHANNEL_EVENT:
|
|
496
|
-
return renderChannelEvent(event);
|
|
497
|
-
case Events.CONTACT_LANGUAGE_CHANGED:
|
|
498
|
-
return renderContactLanguageChangedEvent(event);
|
|
499
|
-
case Events.OPTIN_REQUESTED:
|
|
500
|
-
return renderOptinRequested(event);
|
|
501
|
-
}
|
|
502
|
-
return html `<temba-icon
|
|
503
|
-
name="alert-triangle"
|
|
504
|
-
style="fill:var(--color-error)"
|
|
505
|
-
></temba-icon>
|
|
506
|
-
<div class="description">${event.type}</div>`;
|
|
507
|
-
}
|
|
508
|
-
handleClose(uuid) {
|
|
509
|
-
this.httpComplete = postJSON(`/api/v2/ticket_actions.json`, {
|
|
510
|
-
tickets: [uuid],
|
|
511
|
-
action: 'close'
|
|
512
|
-
})
|
|
513
|
-
.then(() => {
|
|
514
|
-
this.refreshTickets();
|
|
515
|
-
this.refresh();
|
|
516
|
-
this.fireCustomEvent(CustomEventType.ContentChanged, {
|
|
517
|
-
ticket: { uuid, status: 'closed' }
|
|
518
|
-
});
|
|
519
|
-
})
|
|
520
|
-
.catch((response) => {
|
|
521
|
-
console.error(response);
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
checkForAgentAssignmentEvent(agent) {
|
|
525
|
-
this.httpComplete = getAssets(`/api/v2/tickets.json?uuid=${this.ticket}`).then((assets) => {
|
|
526
|
-
if (assets.length === 1) {
|
|
527
|
-
const ticket = assets[0];
|
|
528
|
-
if (ticket.assignee && ticket.assignee.email === agent) {
|
|
529
|
-
this.fireCustomEvent(CustomEventType.ContentChanged, {
|
|
530
|
-
ticket: { uuid: this.ticket, assigned: 'self' }
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
else {
|
|
534
|
-
this.fireCustomEvent(CustomEventType.ContentChanged, {
|
|
535
|
-
ticket: {
|
|
536
|
-
uuid: this.ticket,
|
|
537
|
-
assigned: ticket.assignee ? ticket.assignee : null
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
getEventHandlers() {
|
|
545
|
-
return [
|
|
546
|
-
{
|
|
547
|
-
event: 'scroll',
|
|
548
|
-
method: throttle(this.handleScroll, 50)
|
|
549
|
-
}
|
|
550
|
-
];
|
|
551
|
-
}
|
|
552
|
-
/** Check if a ticket event is no longer represented in a session */
|
|
553
|
-
isPurged(ticket) {
|
|
554
|
-
return !this.ticketEvents[ticket.uuid];
|
|
555
|
-
}
|
|
556
|
-
handleEventClicked(event) {
|
|
557
|
-
const ele = event.target;
|
|
558
|
-
if (ele.tagName == 'IMG') {
|
|
559
|
-
// if we have one, show in our lightbox
|
|
560
|
-
const lightbox = document.querySelector('temba-lightbox');
|
|
561
|
-
if (lightbox) {
|
|
562
|
-
lightbox.showElement(ele);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
renderEventContainer(event) {
|
|
567
|
-
const renderedEvent = html `
|
|
568
|
-
<div
|
|
569
|
-
@click=${this.handleEventClicked}
|
|
570
|
-
class="${this.ticket ? 'active-ticket' : ''} event ${event.type}"
|
|
571
|
-
>
|
|
572
|
-
${this.renderEvent(event)}
|
|
573
|
-
</div>
|
|
574
|
-
${this.debug ? html `<pre>${JSON.stringify(event, null, 2)}</pre>` : null}
|
|
575
|
-
`;
|
|
576
|
-
return renderedEvent;
|
|
577
|
-
}
|
|
578
|
-
render() {
|
|
579
|
-
return html `
|
|
580
|
-
${this.fetching
|
|
581
|
-
? html `<temba-loading units="5" size="10"></temba-loading>`
|
|
582
|
-
: html `<div style="height:0em"></div>`}
|
|
583
|
-
<div class="events" @scroll=${this.handleScroll}>
|
|
584
|
-
${this.eventGroups.map((eventGroup, index) => {
|
|
585
|
-
const grouping = getEventGroupType(eventGroup.events[0], this.ticket);
|
|
586
|
-
const groupIndex = this.eventGroups.length - index - 1;
|
|
587
|
-
const classes = getClasses({
|
|
588
|
-
grouping: true,
|
|
589
|
-
[grouping]: true,
|
|
590
|
-
expanded: eventGroup.open
|
|
591
|
-
});
|
|
592
|
-
return html `<div class="${classes}">
|
|
593
|
-
${grouping === 'verbose'
|
|
594
|
-
? html `<div
|
|
595
|
-
class="event-count"
|
|
596
|
-
@click=${this.handleEventGroupShow}
|
|
597
|
-
data-group-index="${groupIndex}"
|
|
598
|
-
>
|
|
599
|
-
${eventGroup.open
|
|
600
|
-
? html `<temba-icon
|
|
601
|
-
@click=${this.handleEventGroupHide}
|
|
602
|
-
data-group-index="${groupIndex}"
|
|
603
|
-
name="x"
|
|
604
|
-
clickable
|
|
605
|
-
></temba-icon>`
|
|
606
|
-
: html `${eventGroup.events.length}
|
|
607
|
-
${eventGroup.events.length === 1
|
|
608
|
-
? html `event`
|
|
609
|
-
: html `events`} `}
|
|
610
|
-
</div>`
|
|
611
|
-
: null}
|
|
612
|
-
|
|
613
|
-
<div class="items">
|
|
614
|
-
${eventGroup.events.map((event) => {
|
|
615
|
-
if (event.type === Events.TICKET_ASSIGNED &&
|
|
616
|
-
event.note) {
|
|
617
|
-
const noteEvent = { ...event };
|
|
618
|
-
noteEvent.type = Events.TICKET_NOTE_ADDED;
|
|
619
|
-
return html `${this.renderEventContainer(noteEvent)}${this.renderEventContainer(event)}`;
|
|
620
|
-
}
|
|
621
|
-
else {
|
|
622
|
-
return this.renderEventContainer(event);
|
|
623
|
-
}
|
|
624
|
-
})}
|
|
625
|
-
</div>
|
|
626
|
-
</div>`;
|
|
627
|
-
})}
|
|
628
|
-
</div>
|
|
629
|
-
|
|
630
|
-
${this.contact && this.contact.status === 'active'
|
|
631
|
-
? html `<div class="new-messages-container">
|
|
632
|
-
<div
|
|
633
|
-
@click=${() => {
|
|
634
|
-
this.scrollToBottom(true);
|
|
635
|
-
}}
|
|
636
|
-
class="new-messages ${getClasses({
|
|
637
|
-
expanded: this.showMessageAlert
|
|
638
|
-
})}"
|
|
639
|
-
>
|
|
640
|
-
New Messages
|
|
641
|
-
</div>
|
|
642
|
-
</div>`
|
|
643
|
-
: null}
|
|
644
|
-
|
|
645
|
-
</div>
|
|
646
|
-
`;
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
__decorate([
|
|
650
|
-
property({ type: Object })
|
|
651
|
-
], ContactHistory.prototype, "contact", void 0);
|
|
652
|
-
__decorate([
|
|
653
|
-
property({ type: String })
|
|
654
|
-
], ContactHistory.prototype, "uuid", void 0);
|
|
655
|
-
__decorate([
|
|
656
|
-
property({ type: String })
|
|
657
|
-
], ContactHistory.prototype, "agent", void 0);
|
|
658
|
-
__decorate([
|
|
659
|
-
property({ type: Array })
|
|
660
|
-
], ContactHistory.prototype, "eventGroups", void 0);
|
|
661
|
-
__decorate([
|
|
662
|
-
property({ type: Boolean })
|
|
663
|
-
], ContactHistory.prototype, "refreshing", void 0);
|
|
664
|
-
__decorate([
|
|
665
|
-
property({ type: Boolean })
|
|
666
|
-
], ContactHistory.prototype, "fetching", void 0);
|
|
667
|
-
__decorate([
|
|
668
|
-
property({ type: Boolean })
|
|
669
|
-
], ContactHistory.prototype, "complete", void 0);
|
|
670
|
-
__decorate([
|
|
671
|
-
property({ type: String })
|
|
672
|
-
], ContactHistory.prototype, "endpoint", void 0);
|
|
673
|
-
__decorate([
|
|
674
|
-
property({ type: Boolean })
|
|
675
|
-
], ContactHistory.prototype, "debug", void 0);
|
|
676
|
-
__decorate([
|
|
677
|
-
property({ type: Boolean })
|
|
678
|
-
], ContactHistory.prototype, "showMessageAlert", void 0);
|
|
679
|
-
__decorate([
|
|
680
|
-
property({ attribute: false, type: Object })
|
|
681
|
-
], ContactHistory.prototype, "mostRecentEvent", void 0);
|
|
682
|
-
__decorate([
|
|
683
|
-
property({ type: String })
|
|
684
|
-
], ContactHistory.prototype, "ticket", void 0);
|
|
685
|
-
__decorate([
|
|
686
|
-
property({ type: String })
|
|
687
|
-
], ContactHistory.prototype, "endDate", void 0);
|
|
688
|
-
__decorate([
|
|
689
|
-
property({ type: Array })
|
|
690
|
-
], ContactHistory.prototype, "tickets", void 0);
|
|
691
|
-
//# sourceMappingURL=ContactHistory.js.map
|