@hellotext/hellotext 2.3.8 → 2.4.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/README.md +1 -1
- package/dist/hellotext.js +1 -1
- package/dist/webchat-emoji-en.js +1 -0
- package/dist/webchat-emoji-es.js +1 -0
- package/dist/webchat-emoji.js +1 -0
- package/index.d.ts +74 -15
- package/lib/api/webchats.cjs +20 -0
- package/lib/api/webchats.js +20 -0
- package/lib/controllers/mixins/usePopover.cjs +7 -2
- package/lib/controllers/mixins/usePopover.js +7 -2
- package/lib/controllers/webchat/emoji_picker_controller.cjs +49 -10
- package/lib/controllers/webchat/emoji_picker_controller.js +66 -8
- package/lib/controllers/webchat/useBehaviour.cjs +74 -0
- package/lib/controllers/webchat/useBehaviour.js +67 -0
- package/lib/controllers/webchat/useOpeningSequence.cjs +103 -0
- package/lib/controllers/webchat/useOpeningSequence.js +96 -0
- package/lib/controllers/webchat/useTeaser.cjs +163 -0
- package/lib/controllers/webchat/useTeaser.js +156 -0
- package/lib/controllers/webchat_controller.cjs +165 -22
- package/lib/controllers/webchat_controller.js +173 -24
- package/lib/core/configuration/webchat.cjs +117 -42
- package/lib/core/configuration/webchat.js +121 -43
- package/lib/hellotext.cjs +33 -3
- package/lib/hellotext.js +37 -6
- package/lib/models/business.cjs +71 -0
- package/lib/models/business.js +82 -0
- package/lib/models/webchat.cjs +25 -0
- package/lib/models/webchat.js +25 -0
- package/package.json +1 -1
- package/src/api/webchats.js +17 -0
- package/src/controllers/mixins/usePopover.js +4 -2
- package/src/controllers/webchat/emoji_picker_controller.js +51 -9
- package/src/controllers/webchat/useBehaviour.js +81 -0
- package/src/controllers/webchat/useOpeningSequence.js +127 -0
- package/src/controllers/webchat/useTeaser.js +192 -0
- package/src/controllers/webchat_controller.js +187 -23
- package/src/core/configuration/webchat.js +127 -43
- package/src/hellotext.js +38 -3
- package/src/models/business.js +76 -0
- package/src/models/webchat.js +28 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// The teaser markup is already present in the document; this mixin only manages
|
|
2
|
+
// the runtime around it. Keeping the teaser policy here gives the controller one
|
|
3
|
+
// place to delegate click-to-open, the pre-conversation presentation, and timer
|
|
4
|
+
// cleanup. Message sending stays on the controller because it owns the API flow
|
|
5
|
+
// and optimistic customer bubble insertion.
|
|
6
|
+
export var useTeaser = controller => {
|
|
7
|
+
Object.assign(controller, {
|
|
8
|
+
// Called from `connect` after `usePopover` has given the controller `show`.
|
|
9
|
+
// The teaser is optional, so setup first prepares reusable lifecycle state,
|
|
10
|
+
// wires the click surface when present, and then lets eligibility decide
|
|
11
|
+
// whether this session may still show the presentation.
|
|
12
|
+
setupTeaser() {
|
|
13
|
+
this.teaserCycleTimeout = null;
|
|
14
|
+
this.teaserMessages = [];
|
|
15
|
+
this.boundOnTeaserClick = this.boundOnTeaserClick || this.onTeaserClick.bind(this);
|
|
16
|
+
if (!this.hasTeaserTarget) return;
|
|
17
|
+
this.teaserTarget.addEventListener('click', this.boundOnTeaserClick);
|
|
18
|
+
this.startTeaserPresentation();
|
|
19
|
+
},
|
|
20
|
+
// This is the shared teardown path for Stimulus disconnect. Cycling timers
|
|
21
|
+
// and DOM listeners both outlive the current call stack, so they need to be
|
|
22
|
+
// cancelled explicitly when the rendered widget leaves the page.
|
|
23
|
+
teardownTeaser() {
|
|
24
|
+
this.stopTeaserCycle();
|
|
25
|
+
if (this.hasTeaserTarget && this.boundOnTeaserClick) {
|
|
26
|
+
this.teaserTarget.removeEventListener('click', this.boundOnTeaserClick);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
// The current DOM is the source of truth. Each setup pass collects the
|
|
30
|
+
// rendered teaser messages from the teaser surface itself, which keeps the
|
|
31
|
+
// target list small and avoids carrying stale nodes between presentations.
|
|
32
|
+
collectTeaserMessages() {
|
|
33
|
+
if (!this.hasTeaserTarget) return [];
|
|
34
|
+
return Array.from(this.teaserTarget.querySelectorAll('[data-teaser-message]'));
|
|
35
|
+
},
|
|
36
|
+
// The teaser is a one-time, pre-conversation presentation. It can run while
|
|
37
|
+
// the popover is closed, but any active conversation signal makes the teaser
|
|
38
|
+
// ineligible for the rest of this browser session.
|
|
39
|
+
startTeaserPresentation() {
|
|
40
|
+
this.stopTeaserCycle();
|
|
41
|
+
this.teaserMessages = this.collectTeaserMessages();
|
|
42
|
+
if (!this.hasTeaserTarget) return;
|
|
43
|
+
if (this.teaserMessages.length === 0) {
|
|
44
|
+
this.hideTeaser();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (this.openValue) {
|
|
48
|
+
this.dismissTeaserForSession();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (this.conversationIdValue || this.hasRenderedConversationMessages()) {
|
|
52
|
+
this.dismissTeaserForSession();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (this.teaserSeenForSession()) {
|
|
56
|
+
this.hideTeaser();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.teaserTarget.classList.remove('invisible');
|
|
60
|
+
this.showTeaserMessage(0);
|
|
61
|
+
if (this.teaserMessages.length < 2) return;
|
|
62
|
+
this.scheduleNextTeaserMessage(0);
|
|
63
|
+
},
|
|
64
|
+
// Delays belong to the currently visible message, so each teaser controls
|
|
65
|
+
// how long it remains on screen before the next one replaces it. The
|
|
66
|
+
// presentation stops after the last message instead of looping forever.
|
|
67
|
+
scheduleNextTeaserMessage(currentIndex) {
|
|
68
|
+
var nextIndex = currentIndex + 1;
|
|
69
|
+
if (nextIndex >= this.teaserMessages.length) return;
|
|
70
|
+
var currentMessage = this.teaserMessages[currentIndex];
|
|
71
|
+
var delay = this.teaserPresentationDelay(currentMessage);
|
|
72
|
+
this.teaserCycleTimeout = window.setTimeout(() => {
|
|
73
|
+
this.teaserCycleTimeout = null;
|
|
74
|
+
this.showTeaserMessage(nextIndex);
|
|
75
|
+
this.scheduleNextTeaserMessage(nextIndex);
|
|
76
|
+
}, delay);
|
|
77
|
+
},
|
|
78
|
+
// Visibility is managed with the existing `hidden` class for initially
|
|
79
|
+
// concealed teaser messages. This keeps JS from restructuring teaser markup.
|
|
80
|
+
showTeaserMessage(index) {
|
|
81
|
+
this.teaserMessages.forEach((message, messageIndex) => {
|
|
82
|
+
message.classList.toggle('hidden', messageIndex !== index);
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
// Safe to call even when no timer exists. Lifecycle hooks call this before
|
|
86
|
+
// starting, hiding, or tearing down the teaser so only one cycle can be alive.
|
|
87
|
+
stopTeaserCycle() {
|
|
88
|
+
if (this.teaserCycleTimeout === null || this.teaserCycleTimeout === undefined) return;
|
|
89
|
+
window.clearTimeout(this.teaserCycleTimeout);
|
|
90
|
+
this.teaserCycleTimeout = null;
|
|
91
|
+
},
|
|
92
|
+
// Invalid or missing delay values should not break teaser rendering. Treat
|
|
93
|
+
// them as zero, then use a small minimum so zero-delay presentations advance
|
|
94
|
+
// deliberately instead of flashing through every message in one frame.
|
|
95
|
+
teaserMessageDelay(message) {
|
|
96
|
+
var delay = Number(message.dataset.delaySeconds || 0);
|
|
97
|
+
return Number.isFinite(delay) ? delay : 0;
|
|
98
|
+
},
|
|
99
|
+
teaserPresentationDelay(message) {
|
|
100
|
+
return Math.max(this.teaserMessageDelay(message) * 1000, 250);
|
|
101
|
+
},
|
|
102
|
+
// A rendered transcript means the visitor is no longer in the pre-conversation
|
|
103
|
+
// state. The hidden template is deliberately excluded because it is not a live
|
|
104
|
+
// customer-visible message.
|
|
105
|
+
hasRenderedConversationMessages() {
|
|
106
|
+
var messages = [];
|
|
107
|
+
try {
|
|
108
|
+
messages = Array.from(this.messageTargets || []);
|
|
109
|
+
} catch (_error) {
|
|
110
|
+
messages = [];
|
|
111
|
+
}
|
|
112
|
+
return messages.some(message => message !== this.messageTemplateTarget);
|
|
113
|
+
},
|
|
114
|
+
teaserSeenKey() {
|
|
115
|
+
return "hellotext:webchat:".concat(this.idValue || this.element.id, ":teaser-seen");
|
|
116
|
+
},
|
|
117
|
+
teaserSeenForSession() {
|
|
118
|
+
try {
|
|
119
|
+
return window.sessionStorage.getItem(this.teaserSeenKey()) === 'true';
|
|
120
|
+
} catch (_error) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
markTeaserSeenForSession() {
|
|
125
|
+
try {
|
|
126
|
+
window.sessionStorage.setItem(this.teaserSeenKey(), 'true');
|
|
127
|
+
} catch (_error) {
|
|
128
|
+
// Storage can be unavailable in locked-down browsers. The in-memory hide
|
|
129
|
+
// still keeps the active controller from showing the teaser again.
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
// Opening or sending ends the pre-conversation window for this browser
|
|
133
|
+
// session. Incoming message teasers are separate ephemeral content, so they
|
|
134
|
+
// use `hideTeaser`/`updateMessageTeaser` without writing this session flag.
|
|
135
|
+
dismissTeaserForSession() {
|
|
136
|
+
this.markTeaserSeenForSession();
|
|
137
|
+
this.hideTeaser();
|
|
138
|
+
},
|
|
139
|
+
// Stopping the timer before hiding prevents queued presentation steps from
|
|
140
|
+
// flipping hidden messages after the teaser surface has been dismissed.
|
|
141
|
+
hideTeaser() {
|
|
142
|
+
this.stopTeaserCycle();
|
|
143
|
+
if (this.hasTeaserTarget) {
|
|
144
|
+
this.teaserTarget.classList.add('invisible');
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
// A teaser surface click is a user request to open chat, but links inside the
|
|
148
|
+
// teaser already have native browser behavior. Leaving anchors alone keeps
|
|
149
|
+
// external URLs and `tel:` actions working without extra JS.
|
|
150
|
+
onTeaserClick(event) {
|
|
151
|
+
if (event.target.closest('a')) return;
|
|
152
|
+
this.dismissTeaserForSession();
|
|
153
|
+
this.show();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
};
|
|
@@ -11,8 +11,10 @@ var _webchat_channel = _interopRequireDefault(require("../channels/webchat_chann
|
|
|
11
11
|
var _hellotext = _interopRequireDefault(require("../hellotext"));
|
|
12
12
|
var _core = require("../core");
|
|
13
13
|
var _webchat = require("../core/configuration/webchat");
|
|
14
|
-
var _logo_builder = require("../builders/logo_builder");
|
|
15
14
|
var _usePopover = require("./mixins/usePopover");
|
|
15
|
+
var _useBehaviour = require("./webchat/useBehaviour");
|
|
16
|
+
var _useOpeningSequence = require("./webchat/useOpeningSequence");
|
|
17
|
+
var _useTeaser = require("./webchat/useTeaser");
|
|
16
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
19
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
18
20
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
@@ -41,6 +43,7 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
41
43
|
this.messagesAPI = new _messages.default(this.idValue);
|
|
42
44
|
this.webChatChannel = new _webchat_channel.default(this.idValue, _hellotext.default.session, this.conversationIdValue);
|
|
43
45
|
this.files = [];
|
|
46
|
+
this.messageIds = new Set();
|
|
44
47
|
this.onMessageReceived = this.onMessageReceived.bind(this);
|
|
45
48
|
this.onMessageReaction = this.onMessageReaction.bind(this);
|
|
46
49
|
this.onTypingStart = this.onTypingStart.bind(this);
|
|
@@ -52,30 +55,41 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
52
55
|
}, {
|
|
53
56
|
key: "connect",
|
|
54
57
|
value: function connect() {
|
|
58
|
+
(0, _useBehaviour.useBehaviour)(this);
|
|
55
59
|
(0, _usePopover.usePopover)(this);
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
(0, _useOpeningSequence.useOpeningSequence)(this);
|
|
61
|
+
(0, _useTeaser.useTeaser)(this);
|
|
58
62
|
this.setupFloatingUI({
|
|
59
63
|
trigger: this.triggerTarget,
|
|
60
64
|
popover: this.popoverTarget
|
|
61
65
|
});
|
|
66
|
+
if (this.hasTeaserTarget) {
|
|
67
|
+
this.setupFloatingUI({
|
|
68
|
+
trigger: this.triggerTarget,
|
|
69
|
+
popover: this.teaserTarget,
|
|
70
|
+
strategy: 'absolute'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
this.setupTeaser();
|
|
74
|
+
this.setupOpeningSequence();
|
|
62
75
|
this.webChatChannel.onMessage(this.onMessageReceived);
|
|
63
76
|
this.webChatChannel.onTypingStart(this.onTypingStart);
|
|
64
77
|
this.webChatChannel.onReaction(this.onMessageReaction);
|
|
65
78
|
this.messagesContainerTarget.addEventListener('scroll', this.onScroll);
|
|
66
|
-
if (!_hellotext.default.business.features.white_label) {
|
|
67
|
-
this.toolbarTarget.appendChild(_logo_builder.LogoBuilder.build());
|
|
68
|
-
}
|
|
69
79
|
if (this.shouldOpenOnMount) {
|
|
70
80
|
this.openValue = true;
|
|
71
81
|
}
|
|
72
82
|
_hellotext.default.eventEmitter.dispatch('webchat:mounted');
|
|
73
83
|
this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent);
|
|
84
|
+
this.scheduleBehaviourOpen();
|
|
74
85
|
_get(_getPrototypeOf(_default.prototype), "connect", this).call(this);
|
|
75
86
|
}
|
|
76
87
|
}, {
|
|
77
88
|
key: "disconnect",
|
|
78
89
|
value: function disconnect() {
|
|
90
|
+
this.cancelBehaviourOpen();
|
|
91
|
+
this.teardownTeaser();
|
|
92
|
+
this.teardownOpeningSequence();
|
|
79
93
|
this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
|
|
80
94
|
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
|
|
81
95
|
|
|
@@ -221,11 +235,12 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
221
235
|
}
|
|
222
236
|
if (attachments) {
|
|
223
237
|
attachments.forEach(attachmentUrl => {
|
|
238
|
+
var _this$messageAttachme;
|
|
224
239
|
const image = this.attachmentImageTarget.cloneNode(true);
|
|
225
240
|
image.removeAttribute('data-hellotext--webchat-target');
|
|
226
241
|
image.src = attachmentUrl;
|
|
227
242
|
image.style.display = 'block';
|
|
228
|
-
|
|
243
|
+
(_this$messageAttachme = this.messageAttachmentsContainer(element)) === null || _this$messageAttachme === void 0 ? void 0 : _this$messageAttachme.appendChild(image);
|
|
229
244
|
});
|
|
230
245
|
}
|
|
231
246
|
element.setAttribute('data-body', body);
|
|
@@ -240,7 +255,7 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
240
255
|
}, {
|
|
241
256
|
key: "onClickOutside",
|
|
242
257
|
value: function onClickOutside(event) {
|
|
243
|
-
if (_webchat.Webchat.
|
|
258
|
+
if (_webchat.Webchat.mode === _webchat.modes.POPOVER && this.openValue && event.target.nodeType && this.element.contains(event.target) === false) {
|
|
244
259
|
this.openValue = false;
|
|
245
260
|
}
|
|
246
261
|
}
|
|
@@ -255,7 +270,9 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
255
270
|
}, {
|
|
256
271
|
key: "onPopoverOpened",
|
|
257
272
|
value: function onPopoverOpened() {
|
|
273
|
+
var _this$dismissTeaserFo;
|
|
258
274
|
this.popoverTarget.classList.remove(...this.fadeOutClasses);
|
|
275
|
+
(_this$dismissTeaserFo = this.dismissTeaserForSession) === null || _this$dismissTeaserFo === void 0 ? void 0 : _this$dismissTeaserFo.call(this);
|
|
259
276
|
if (!this.onMobile) {
|
|
260
277
|
this.inputTarget.focus();
|
|
261
278
|
}
|
|
@@ -270,6 +287,10 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
270
287
|
}
|
|
271
288
|
_hellotext.default.eventEmitter.dispatch('webchat:opened');
|
|
272
289
|
localStorage.setItem(`hellotext--webchat--${this.idValue}`, 'opened');
|
|
290
|
+
if (this.messageTeaserValue) {
|
|
291
|
+
this.messageTeaserValue = null;
|
|
292
|
+
}
|
|
293
|
+
this.startOpeningSequence();
|
|
273
294
|
if (this.unreadCounterTarget.style.display === 'none') return;
|
|
274
295
|
this.unreadCounterTarget.style.display = 'none';
|
|
275
296
|
this.unreadCounterTarget.innerText = '0';
|
|
@@ -308,11 +329,15 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
308
329
|
}, {
|
|
309
330
|
key: "onMessageReceived",
|
|
310
331
|
value: function onMessageReceived(message) {
|
|
332
|
+
var _this$hideTeaser;
|
|
311
333
|
const {
|
|
312
334
|
id,
|
|
313
335
|
body,
|
|
314
|
-
attachments
|
|
336
|
+
attachments,
|
|
337
|
+
teaser
|
|
315
338
|
} = message;
|
|
339
|
+
if (!this.claimMessageId(id)) return;
|
|
340
|
+
(_this$hideTeaser = this.hideTeaser) === null || _this$hideTeaser === void 0 ? void 0 : _this$hideTeaser.call(this);
|
|
316
341
|
if (message.carousel) {
|
|
317
342
|
return this.insertCarouselMessage(message);
|
|
318
343
|
}
|
|
@@ -321,13 +346,15 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
321
346
|
const element = this.messageTemplateTarget.cloneNode(true);
|
|
322
347
|
element.style.display = 'flex';
|
|
323
348
|
element.querySelector('[data-body]').innerHTML = div.innerHTML;
|
|
349
|
+
element.setAttribute('data-id', id);
|
|
324
350
|
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
325
351
|
if (attachments) {
|
|
326
352
|
attachments.forEach(attachmentUrl => {
|
|
353
|
+
var _this$messageAttachme2;
|
|
327
354
|
const image = this.attachmentImageTarget.cloneNode(true);
|
|
328
355
|
image.src = attachmentUrl;
|
|
329
356
|
image.style.display = 'block';
|
|
330
|
-
|
|
357
|
+
(_this$messageAttachme2 = this.messageAttachmentsContainer(element)) === null || _this$messageAttachme2 === void 0 ? void 0 : _this$messageAttachme2.appendChild(image);
|
|
331
358
|
});
|
|
332
359
|
}
|
|
333
360
|
this.clearTypingIndicator();
|
|
@@ -339,13 +366,33 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
339
366
|
element.scrollIntoView({
|
|
340
367
|
behavior: 'smooth'
|
|
341
368
|
});
|
|
369
|
+
this.updateMessageTeaser(teaser);
|
|
342
370
|
if (this.openValue) {
|
|
343
371
|
this.messagesAPI.markAsSeen(id);
|
|
344
372
|
return;
|
|
345
373
|
}
|
|
346
|
-
this.
|
|
347
|
-
|
|
348
|
-
|
|
374
|
+
this.incrementUnreadCounter();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// TCP broadcasts may deliver the same incoming message twice before Stimulus
|
|
378
|
+
// has connected the appended message target. Claiming the id in memory closes
|
|
379
|
+
// that window, while the target check still drops messages rendered earlier.
|
|
380
|
+
}, {
|
|
381
|
+
key: "claimMessageId",
|
|
382
|
+
value: function claimMessageId(id) {
|
|
383
|
+
const messageTargets = this.messageTargets || [];
|
|
384
|
+
if (this.messageIds.has(id)) return false;
|
|
385
|
+
this.messageIds.add(id);
|
|
386
|
+
return !messageTargets.some(element => element.dataset.id === id);
|
|
387
|
+
}
|
|
388
|
+
}, {
|
|
389
|
+
key: "updateMessageTeaser",
|
|
390
|
+
value: function updateMessageTeaser(teaser) {
|
|
391
|
+
this.messageTeaserValue = teaser;
|
|
392
|
+
if (this.messageTeaserValue && this.hasTeaserTarget) {
|
|
393
|
+
this.teaserTarget.innerHTML = this.messageTeaserValue;
|
|
394
|
+
this.teaserTarget.classList.toggle('invisible', this.openValue);
|
|
395
|
+
}
|
|
349
396
|
}
|
|
350
397
|
}, {
|
|
351
398
|
key: "insertCarouselMessage",
|
|
@@ -353,6 +400,8 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
353
400
|
var _element$querySelecto;
|
|
354
401
|
const html = message.html;
|
|
355
402
|
const element = new DOMParser().parseFromString(html, 'text/html').body.firstElementChild;
|
|
403
|
+
element.setAttribute('data-id', message.id);
|
|
404
|
+
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
356
405
|
this.clearTypingIndicator();
|
|
357
406
|
this.messagesContainerTarget.appendChild(element);
|
|
358
407
|
element.scrollIntoView({
|
|
@@ -362,13 +411,12 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
362
411
|
...message,
|
|
363
412
|
body: ((_element$querySelecto = element.querySelector('[data-body]')) === null || _element$querySelecto === void 0 ? void 0 : _element$querySelecto.innerText) || ''
|
|
364
413
|
});
|
|
414
|
+
this.updateMessageTeaser(message.teaser);
|
|
365
415
|
if (this.openValue) {
|
|
366
416
|
this.messagesAPI.markAsSeen(message.id);
|
|
367
417
|
return;
|
|
368
418
|
}
|
|
369
|
-
this.
|
|
370
|
-
const unreadCount = (parseInt(this.unreadCounterTarget.innerText) || 0) + 1;
|
|
371
|
-
this.unreadCounterTarget.innerText = unreadCount > 99 ? '99+' : unreadCount;
|
|
419
|
+
this.incrementUnreadCounter();
|
|
372
420
|
}
|
|
373
421
|
}, {
|
|
374
422
|
key: "resizeInput",
|
|
@@ -394,7 +442,8 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
394
442
|
cardElement
|
|
395
443
|
}
|
|
396
444
|
}) {
|
|
397
|
-
var _cardElement$querySel;
|
|
445
|
+
var _this$dismissTeaserFo2, _cardElement$querySel;
|
|
446
|
+
(_this$dismissTeaserFo2 = this.dismissTeaserForSession) === null || _this$dismissTeaserFo2 === void 0 ? void 0 : _this$dismissTeaserFo2.call(this);
|
|
398
447
|
const formData = new FormData();
|
|
399
448
|
formData.append('message[body]', body);
|
|
400
449
|
formData.append('message[replied_to]', id);
|
|
@@ -402,13 +451,15 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
402
451
|
formData.append('message[button]', buttonId);
|
|
403
452
|
formData.append('session', _hellotext.default.session);
|
|
404
453
|
formData.append('locale', _core.Locale.toString());
|
|
454
|
+
this.appendOpeningSequenceMessageIds(formData);
|
|
405
455
|
const element = this.buildMessageElement();
|
|
406
456
|
const attachment = (_cardElement$querySel = cardElement.querySelector('img')) === null || _cardElement$querySel === void 0 ? void 0 : _cardElement$querySel.cloneNode(true);
|
|
407
457
|
element.querySelector('[data-body]').innerText = body;
|
|
408
458
|
if (attachment) {
|
|
459
|
+
var _this$messageAttachme3;
|
|
409
460
|
attachment.removeAttribute('width');
|
|
410
461
|
attachment.removeAttribute('height');
|
|
411
|
-
|
|
462
|
+
(_this$messageAttachme3 = this.messageAttachmentsContainer(element)) === null || _this$messageAttachme3 === void 0 ? void 0 : _this$messageAttachme3.appendChild(attachment);
|
|
412
463
|
}
|
|
413
464
|
if (this.typingIndicatorVisible && this.hasTypingIndicatorTarget) {
|
|
414
465
|
this.messagesContainerTarget.insertBefore(element, this.typingIndicatorTarget);
|
|
@@ -437,6 +488,7 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
437
488
|
target: element,
|
|
438
489
|
detail: data.id
|
|
439
490
|
});
|
|
491
|
+
this.clearRevealedOpeningSequenceMessageIds();
|
|
440
492
|
const message = {
|
|
441
493
|
id: data.id,
|
|
442
494
|
body: body,
|
|
@@ -448,10 +500,80 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
448
500
|
};
|
|
449
501
|
_hellotext.default.eventEmitter.dispatch('webchat:message:sent', message);
|
|
450
502
|
}
|
|
503
|
+
}, {
|
|
504
|
+
key: "sendTeaserQuickReply",
|
|
505
|
+
value: async function sendTeaserQuickReply(event) {
|
|
506
|
+
var _this$dismissTeaserFo3;
|
|
507
|
+
event.preventDefault();
|
|
508
|
+
event.stopPropagation();
|
|
509
|
+
const button = event.currentTarget;
|
|
510
|
+
const value = (button.dataset.value || '').trim();
|
|
511
|
+
const label = [button.dataset.text, button.textContent].map(text => (text || '').trim()).find(text => text.length > 0);
|
|
512
|
+
const text = value || label;
|
|
513
|
+
if (!text) return;
|
|
514
|
+
(_this$dismissTeaserFo3 = this.dismissTeaserForSession) === null || _this$dismissTeaserFo3 === void 0 ? void 0 : _this$dismissTeaserFo3.call(this);
|
|
515
|
+
this.show();
|
|
516
|
+
const buttonType = (button.dataset.type || '').trim() || 'quick_reply';
|
|
517
|
+
const formData = new FormData();
|
|
518
|
+
formData.append('message[body]', text);
|
|
519
|
+
formData.append('session', _hellotext.default.session);
|
|
520
|
+
formData.append('locale', _core.Locale.toString());
|
|
521
|
+
this.appendOpeningSequenceMessageIds(formData);
|
|
522
|
+
const element = this.buildMessageElement();
|
|
523
|
+
element.querySelector('[data-body]').innerText = text;
|
|
524
|
+
if (this.typingIndicatorVisible && this.hasTypingIndicatorTarget) {
|
|
525
|
+
this.messagesContainerTarget.insertBefore(element, this.typingIndicatorTarget);
|
|
526
|
+
} else {
|
|
527
|
+
this.messagesContainerTarget.appendChild(element);
|
|
528
|
+
}
|
|
529
|
+
element.scrollIntoView({
|
|
530
|
+
behavior: 'smooth'
|
|
531
|
+
});
|
|
532
|
+
this.broadcastChannel.postMessage({
|
|
533
|
+
type: 'message:sent',
|
|
534
|
+
element: element.outerHTML
|
|
535
|
+
});
|
|
536
|
+
if (!this.typingIndicatorVisible) {
|
|
537
|
+
clearTimeout(this.optimisticTypingTimeout);
|
|
538
|
+
this.optimisticTypingTimeout = setTimeout(() => {
|
|
539
|
+
this.showOptimisticTypingIndicator();
|
|
540
|
+
}, this.optimisticTypingIndicatorWaitValue);
|
|
541
|
+
}
|
|
542
|
+
const response = await this.messagesAPI.create(formData);
|
|
543
|
+
if (response.failed) {
|
|
544
|
+
clearTimeout(this.optimisticTypingTimeout);
|
|
545
|
+
this.broadcastChannel.postMessage({
|
|
546
|
+
type: 'message:failed',
|
|
547
|
+
id: element.id
|
|
548
|
+
});
|
|
549
|
+
return element.classList.add('failed');
|
|
550
|
+
}
|
|
551
|
+
const data = await response.json();
|
|
552
|
+
element.setAttribute('data-id', data.id);
|
|
553
|
+
this.clearRevealedOpeningSequenceMessageIds();
|
|
554
|
+
_hellotext.default.eventEmitter.dispatch('webchat:message:sent', {
|
|
555
|
+
id: data.id,
|
|
556
|
+
body: text,
|
|
557
|
+
attachments: [],
|
|
558
|
+
type: 'quick_reply',
|
|
559
|
+
teaser: {
|
|
560
|
+
text: label || text,
|
|
561
|
+
value: value || text,
|
|
562
|
+
type: buttonType
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
if (data.conversation && data.conversation !== this.conversationIdValue) {
|
|
566
|
+
this.conversationIdValue = data.conversation;
|
|
567
|
+
this.webChatChannel.updateSubscriptionWith(this.conversationIdValue);
|
|
568
|
+
}
|
|
569
|
+
if (this.typingIndicatorVisible) {
|
|
570
|
+
this.resetTypingIndicatorTimer();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
451
573
|
}, {
|
|
452
574
|
key: "sendMessage",
|
|
453
575
|
value: async function sendMessage(e) {
|
|
454
|
-
|
|
576
|
+
var _this$dismissTeaserFo4;
|
|
455
577
|
const message = {
|
|
456
578
|
body: this.inputTarget.value,
|
|
457
579
|
attachments: this.files
|
|
@@ -462,6 +584,8 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
462
584
|
}
|
|
463
585
|
return;
|
|
464
586
|
}
|
|
587
|
+
(_this$dismissTeaserFo4 = this.dismissTeaserForSession) === null || _this$dismissTeaserFo4 === void 0 ? void 0 : _this$dismissTeaserFo4.call(this);
|
|
588
|
+
const formData = new FormData();
|
|
465
589
|
if (this.inputTarget.value.trim().length > 0) {
|
|
466
590
|
formData.append('message[body]', this.inputTarget.value);
|
|
467
591
|
} else {
|
|
@@ -472,6 +596,7 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
472
596
|
});
|
|
473
597
|
formData.append('session', _hellotext.default.session);
|
|
474
598
|
formData.append('locale', _core.Locale.toString());
|
|
599
|
+
this.appendOpeningSequenceMessageIds(formData);
|
|
475
600
|
const element = this.buildMessageElement();
|
|
476
601
|
if (this.inputTarget.value.trim().length > 0) {
|
|
477
602
|
element.querySelector('[data-body]').innerText = this.inputTarget.value;
|
|
@@ -481,7 +606,8 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
481
606
|
const attachments = this.attachmentContainerTarget.querySelectorAll('img');
|
|
482
607
|
if (attachments.length > 0) {
|
|
483
608
|
attachments.forEach(attachment => {
|
|
484
|
-
|
|
609
|
+
var _this$messageAttachme4;
|
|
610
|
+
(_this$messageAttachme4 = this.messageAttachmentsContainer(element)) === null || _this$messageAttachme4 === void 0 ? void 0 : _this$messageAttachme4.appendChild(attachment.cloneNode(true));
|
|
485
611
|
});
|
|
486
612
|
}
|
|
487
613
|
|
|
@@ -528,6 +654,7 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
528
654
|
const data = await response.json();
|
|
529
655
|
element.setAttribute('data-id', data.id);
|
|
530
656
|
message.id = data.id;
|
|
657
|
+
this.clearRevealedOpeningSequenceMessageIds();
|
|
531
658
|
_hellotext.default.eventEmitter.dispatch('webchat:message:sent', message);
|
|
532
659
|
if (data.conversation !== this.conversationIdValue) {
|
|
533
660
|
this.conversationIdValue = data.conversation;
|
|
@@ -551,6 +678,18 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
551
678
|
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
552
679
|
return element;
|
|
553
680
|
}
|
|
681
|
+
}, {
|
|
682
|
+
key: "messageAttachmentsContainer",
|
|
683
|
+
value: function messageAttachmentsContainer(element) {
|
|
684
|
+
return element.querySelector('[data-attachments-container], [data-attachment-container]');
|
|
685
|
+
}
|
|
686
|
+
}, {
|
|
687
|
+
key: "incrementUnreadCounter",
|
|
688
|
+
value: function incrementUnreadCounter() {
|
|
689
|
+
this.unreadCounterTarget.style.display = 'flex';
|
|
690
|
+
const unreadCount = (parseInt(this.unreadCounterTarget.innerText) || 0) + 1;
|
|
691
|
+
this.unreadCounterTarget.innerText = Math.min(unreadCount, 9);
|
|
692
|
+
}
|
|
554
693
|
}, {
|
|
555
694
|
key: "openAttachment",
|
|
556
695
|
value: function openAttachment() {
|
|
@@ -716,7 +855,11 @@ _default.values = {
|
|
|
716
855
|
optimisticTypingIndicatorWait: {
|
|
717
856
|
type: Number,
|
|
718
857
|
default: 1000
|
|
719
|
-
}
|
|
858
|
+
},
|
|
859
|
+
// 1 second,
|
|
860
|
+
teaser: Object,
|
|
861
|
+
messageTeaser: String,
|
|
862
|
+
behaviour: Object
|
|
720
863
|
};
|
|
721
864
|
_default.classes = ['fadeOut'];
|
|
722
|
-
_default.targets = ['trigger', 'popover', 'input', 'attachmentInput', 'attachmentButton', 'errorMessageContainer', 'attachmentTemplate', 'attachmentContainer', 'attachment', 'messageTemplate', 'messagesContainer', 'title', 'attachmentImage', 'footer', 'toolbar', 'message', 'unreadCounter', 'typingIndicator', 'typingIndicatorTemplate'];
|
|
865
|
+
_default.targets = ['trigger', 'popover', 'input', 'attachmentInput', 'attachmentButton', 'errorMessageContainer', 'attachmentTemplate', 'attachmentContainer', 'attachment', 'messageTemplate', 'messagesContainer', 'title', 'attachmentImage', 'footer', 'toolbar', 'message', 'unreadCounter', 'typingIndicator', 'typingIndicatorTemplate', 'teaser', 'openingSequence', 'openingSequenceMessage'];
|