@hellotext/hellotext 2.4.2 → 2.4.4
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/dist/hellotext.js +1 -1
- package/lib/controllers/message_controller.cjs +144 -25
- package/lib/controllers/message_controller.js +145 -25
- package/lib/controllers/mixins/usePopover.cjs +2 -0
- package/lib/controllers/mixins/usePopover.js +2 -0
- package/lib/controllers/webchat/useTeaser.cjs +3 -1
- package/lib/controllers/webchat/useTeaser.js +3 -1
- package/lib/controllers/webchat_controller.cjs +219 -25
- package/lib/controllers/webchat_controller.js +233 -25
- package/lib/core/configuration.cjs +19 -0
- package/lib/core/configuration.js +19 -0
- package/lib/models/business.cjs +86 -5
- package/lib/models/business.js +82 -5
- package/lib/models/webchat.cjs +18 -3
- package/lib/models/webchat.js +27 -6
- package/package.json +1 -1
- package/src/controllers/message_controller.js +145 -22
- package/src/controllers/mixins/usePopover.js +1 -0
- package/src/controllers/webchat/useTeaser.js +4 -1
- package/src/controllers/webchat_controller.js +255 -27
- package/src/core/configuration.js +25 -0
- package/src/models/business.js +86 -5
- package/src/models/webchat.js +22 -3
|
@@ -28,6 +28,15 @@ import { usePopover } from './mixins/usePopover';
|
|
|
28
28
|
import { useBehaviour } from './webchat/useBehaviour';
|
|
29
29
|
import { useOpeningSequence } from './webchat/useOpeningSequence';
|
|
30
30
|
import { useTeaser } from './webchat/useTeaser';
|
|
31
|
+
var POPOVER_ANIMATION_DURATION = 120;
|
|
32
|
+
var MESSAGE_TIMESTAMP_FORMAT_OPTIONS = {
|
|
33
|
+
hour: 'numeric',
|
|
34
|
+
minute: '2-digit'
|
|
35
|
+
};
|
|
36
|
+
var SCROLL_ISOLATION_EVENT_OPTIONS = {
|
|
37
|
+
capture: true,
|
|
38
|
+
passive: true
|
|
39
|
+
};
|
|
31
40
|
var _default = /*#__PURE__*/function (_Controller) {
|
|
32
41
|
_inherits(_default, _Controller);
|
|
33
42
|
var _super = _createSuper(_default);
|
|
@@ -47,6 +56,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
47
56
|
this.onTypingStart = this.onTypingStart.bind(this);
|
|
48
57
|
this.onScroll = this.onScroll.bind(this);
|
|
49
58
|
this.onOutboundMessageSent = this.onOutboundMessageSent.bind(this);
|
|
59
|
+
this.closePopoverOnEscape = this.closePopoverOnEscape.bind(this);
|
|
50
60
|
this.broadcastChannel = new BroadcastChannel("hellotext--webchat--".concat(this.idValue));
|
|
51
61
|
_get(_getPrototypeOf(_default.prototype), "initialize", this).call(this);
|
|
52
62
|
}
|
|
@@ -70,15 +80,20 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
70
80
|
}
|
|
71
81
|
this.setupTeaser();
|
|
72
82
|
this.setupOpeningSequence();
|
|
83
|
+
this.localizeMessageTimestamps();
|
|
73
84
|
this.webChatChannel.onMessage(this.onMessageReceived);
|
|
74
85
|
this.webChatChannel.onTypingStart(this.onTypingStart);
|
|
75
86
|
this.webChatChannel.onReaction(this.onMessageReaction);
|
|
87
|
+
this.setupMessagesContainerScrollIsolation();
|
|
76
88
|
this.messagesContainerTarget.addEventListener('scroll', this.onScroll);
|
|
89
|
+
this.messagesContainerTarget.addEventListener('wheel', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
|
|
90
|
+
this.messagesContainerTarget.addEventListener('touchmove', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
|
|
77
91
|
if (this.shouldOpenOnMount) {
|
|
78
92
|
this.openValue = true;
|
|
79
93
|
}
|
|
80
94
|
Hellotext.eventEmitter.dispatch('webchat:mounted');
|
|
81
95
|
this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent);
|
|
96
|
+
window.addEventListener('keydown', this.closePopoverOnEscape, true);
|
|
82
97
|
this.scheduleBehaviourOpen();
|
|
83
98
|
_get(_getPrototypeOf(_default.prototype), "connect", this).call(this);
|
|
84
99
|
}
|
|
@@ -86,10 +101,14 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
86
101
|
key: "disconnect",
|
|
87
102
|
value: function disconnect() {
|
|
88
103
|
this.cancelBehaviourOpen();
|
|
104
|
+
this.clearPopoverOpenAnimation();
|
|
89
105
|
this.teardownTeaser();
|
|
90
106
|
this.teardownOpeningSequence();
|
|
91
107
|
this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
|
|
92
108
|
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
|
|
109
|
+
this.messagesContainerTarget.removeEventListener('wheel', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
|
|
110
|
+
this.messagesContainerTarget.removeEventListener('touchmove', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
|
|
111
|
+
window.removeEventListener('keydown', this.closePopoverOnEscape, true);
|
|
93
112
|
|
|
94
113
|
// Clean up typing indicator timeouts
|
|
95
114
|
this.clearTypingIndicator();
|
|
@@ -97,6 +116,21 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
97
116
|
this.floatingUICleanup();
|
|
98
117
|
_get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this);
|
|
99
118
|
}
|
|
119
|
+
}, {
|
|
120
|
+
key: "setupMessagesContainerScrollIsolation",
|
|
121
|
+
value: function setupMessagesContainerScrollIsolation() {
|
|
122
|
+
this.messagesContainerTarget.style.overscrollBehavior = 'contain';
|
|
123
|
+
this.messagesContainerTarget.style.webkitOverflowScrolling = 'touch';
|
|
124
|
+
this.messagesContainerTarget.style.touchAction = 'pan-y';
|
|
125
|
+
this.messagesContainerTarget.setAttribute('data-lenis-prevent', '');
|
|
126
|
+
this.messagesContainerTarget.setAttribute('data-lenis-prevent-wheel', '');
|
|
127
|
+
this.messagesContainerTarget.setAttribute('data-lenis-prevent-touch', '');
|
|
128
|
+
}
|
|
129
|
+
}, {
|
|
130
|
+
key: "stopHostScrollPropagation",
|
|
131
|
+
value: function stopHostScrollPropagation(event) {
|
|
132
|
+
event.stopPropagation();
|
|
133
|
+
}
|
|
100
134
|
}, {
|
|
101
135
|
key: "onTypingStart",
|
|
102
136
|
value: function onTypingStart() {
|
|
@@ -178,6 +212,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
178
212
|
var callbacks = {
|
|
179
213
|
'message:sent': data => {
|
|
180
214
|
var element = new DOMParser().parseFromString(data.element, 'text/html').body.firstElementChild;
|
|
215
|
+
this.localizeMessageTimestamps(element);
|
|
181
216
|
|
|
182
217
|
// Insert message before typing indicator if one exists
|
|
183
218
|
if (this.typingIndicatorVisible && this.hasTypingIndicatorTarget) {
|
|
@@ -190,8 +225,8 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
190
225
|
});
|
|
191
226
|
},
|
|
192
227
|
'message:failed': data => {
|
|
193
|
-
var
|
|
194
|
-
|
|
228
|
+
var element = this.messagesContainerTarget.querySelector("#".concat(data.id));
|
|
229
|
+
this.markMessageFailed(element, data.reason);
|
|
195
230
|
}
|
|
196
231
|
};
|
|
197
232
|
if (callbacks[data.type]) {
|
|
@@ -221,6 +256,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
221
256
|
body,
|
|
222
257
|
attachments
|
|
223
258
|
} = message;
|
|
259
|
+
var createdAt = message.created_at || message.createdAt;
|
|
224
260
|
var div = document.createElement('div');
|
|
225
261
|
div.innerHTML = body;
|
|
226
262
|
var element = this.messageTemplateTarget.cloneNode(true);
|
|
@@ -243,6 +279,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
243
279
|
});
|
|
244
280
|
}
|
|
245
281
|
element.setAttribute('data-body', body);
|
|
282
|
+
this.localizeMessageTimestamp(element.querySelector('[data-message-timestamp]'), createdAt);
|
|
246
283
|
this.messagesContainerTarget.prepend(element);
|
|
247
284
|
});
|
|
248
285
|
this.messagesContainerTarget.scroll({
|
|
@@ -266,10 +303,30 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
266
303
|
}, {
|
|
267
304
|
key: "closePopover",
|
|
268
305
|
value: function closePopover() {
|
|
269
|
-
this.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
306
|
+
this.clearPopoverOpenAnimation();
|
|
307
|
+
this.popoverTarget.classList.remove(...this.fadeOutClasses);
|
|
308
|
+
this.openValue = false;
|
|
309
|
+
}
|
|
310
|
+
}, {
|
|
311
|
+
key: "preparePopoverOpenAnimation",
|
|
312
|
+
value: function preparePopoverOpenAnimation() {
|
|
313
|
+
this.clearPopoverOpenAnimation();
|
|
314
|
+
this.popoverTarget.classList.remove(...this.fadeOutClasses);
|
|
315
|
+
this.popoverTarget.classList.add('hellotext--webchat-popover-opening');
|
|
316
|
+
this.popoverOpenAnimationTimeout = setTimeout(() => {
|
|
317
|
+
this.popoverTarget.classList.remove('hellotext--webchat-popover-opening');
|
|
318
|
+
this.popoverOpenAnimationTimeout = null;
|
|
319
|
+
}, POPOVER_ANIMATION_DURATION);
|
|
320
|
+
}
|
|
321
|
+
}, {
|
|
322
|
+
key: "clearPopoverOpenAnimation",
|
|
323
|
+
value: function clearPopoverOpenAnimation() {
|
|
324
|
+
var _this$popoverTarget;
|
|
325
|
+
if (this.popoverOpenAnimationTimeout) {
|
|
326
|
+
clearTimeout(this.popoverOpenAnimationTimeout);
|
|
327
|
+
this.popoverOpenAnimationTimeout = null;
|
|
328
|
+
}
|
|
329
|
+
(_this$popoverTarget = this.popoverTarget) === null || _this$popoverTarget === void 0 ? void 0 : _this$popoverTarget.classList.remove('hellotext--webchat-popover-opening');
|
|
273
330
|
}
|
|
274
331
|
}, {
|
|
275
332
|
key: "onPopoverOpened",
|
|
@@ -303,6 +360,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
303
360
|
}, {
|
|
304
361
|
key: "onPopoverClosed",
|
|
305
362
|
value: function onPopoverClosed() {
|
|
363
|
+
this.clearPopoverOpenAnimation();
|
|
306
364
|
Hellotext.eventEmitter.dispatch('webchat:closed');
|
|
307
365
|
localStorage.setItem("hellotext--webchat--".concat(this.idValue), 'closed');
|
|
308
366
|
}
|
|
@@ -340,6 +398,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
340
398
|
attachments,
|
|
341
399
|
teaser
|
|
342
400
|
} = message;
|
|
401
|
+
var createdAt = message.created_at || message.createdAt;
|
|
343
402
|
if (!this.claimMessageId(id)) return;
|
|
344
403
|
(_this$hideTeaser = this.hideTeaser) === null || _this$hideTeaser === void 0 ? void 0 : _this$hideTeaser.call(this);
|
|
345
404
|
if (message.carousel) {
|
|
@@ -352,6 +411,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
352
411
|
element.querySelector('[data-body]').innerHTML = div.innerHTML;
|
|
353
412
|
element.setAttribute('data-id', id);
|
|
354
413
|
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
414
|
+
this.localizeMessageTimestamp(element.querySelector('[data-message-timestamp]'), createdAt);
|
|
355
415
|
if (attachments) {
|
|
356
416
|
attachments.forEach(attachmentUrl => {
|
|
357
417
|
var _this$messageAttachme2;
|
|
@@ -408,6 +468,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
408
468
|
var element = new DOMParser().parseFromString(html, 'text/html').body.firstElementChild;
|
|
409
469
|
element.setAttribute('data-id', message.id);
|
|
410
470
|
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
471
|
+
this.localizeMessageTimestamps(element);
|
|
411
472
|
this.clearTypingIndicator();
|
|
412
473
|
this.messagesContainerTarget.appendChild(element);
|
|
413
474
|
element.scrollIntoView({
|
|
@@ -453,14 +514,14 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
453
514
|
(_this$dismissTeaserFo2 = this.dismissTeaserForSession) === null || _this$dismissTeaserFo2 === void 0 ? void 0 : _this$dismissTeaserFo2.call(this);
|
|
454
515
|
var formData = new FormData();
|
|
455
516
|
formData.append('message[body]', body);
|
|
456
|
-
formData.append('message[replied_to]', id);
|
|
457
|
-
formData.append('message[product]', product);
|
|
458
|
-
formData.append('message[button]', buttonId);
|
|
517
|
+
if (id) formData.append('message[replied_to]', id);
|
|
518
|
+
if (product) formData.append('message[product]', product);
|
|
519
|
+
if (buttonId) formData.append('message[button]', buttonId);
|
|
459
520
|
formData.append('session', Hellotext.session);
|
|
460
521
|
formData.append('locale', Locale.toString());
|
|
461
522
|
this.appendOpeningSequenceMessageIds(formData);
|
|
462
523
|
var element = this.buildMessageElement();
|
|
463
|
-
var attachment = (_cardElement$querySel = cardElement.querySelector('img')) === null || _cardElement$querySel === void 0 ? void 0 : _cardElement$querySel.cloneNode(true);
|
|
524
|
+
var attachment = cardElement === null || cardElement === void 0 ? void 0 : (_cardElement$querySel = cardElement.querySelector('img')) === null || _cardElement$querySel === void 0 ? void 0 : _cardElement$querySel.cloneNode(true);
|
|
464
525
|
element.querySelector('[data-body]').innerText = body;
|
|
465
526
|
if (attachment) {
|
|
466
527
|
var _this$messageAttachme3;
|
|
@@ -484,17 +545,14 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
484
545
|
if (response.failed) {
|
|
485
546
|
// Clear the optimistic typing indicator on failure
|
|
486
547
|
clearTimeout(this.optimisticTypingTimeout);
|
|
487
|
-
this.
|
|
488
|
-
type: 'message:failed',
|
|
489
|
-
id: element.id
|
|
490
|
-
});
|
|
491
|
-
return element.classList.add('failed');
|
|
548
|
+
return this.markMessageFailedFromResponse(response, element);
|
|
492
549
|
}
|
|
493
550
|
var data = yield response.json();
|
|
494
551
|
this.dispatch('set:id', {
|
|
495
552
|
target: element,
|
|
496
553
|
detail: data.id
|
|
497
554
|
});
|
|
555
|
+
this.localizeMessageTimestamp(element.querySelector('[data-message-timestamp]'), data.created_at || data.createdAt);
|
|
498
556
|
this.clearRevealedOpeningSequenceMessageIds();
|
|
499
557
|
var message = {
|
|
500
558
|
id: data.id,
|
|
@@ -555,14 +613,11 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
555
613
|
var response = yield this.messagesAPI.create(formData);
|
|
556
614
|
if (response.failed) {
|
|
557
615
|
clearTimeout(this.optimisticTypingTimeout);
|
|
558
|
-
this.
|
|
559
|
-
type: 'message:failed',
|
|
560
|
-
id: element.id
|
|
561
|
-
});
|
|
562
|
-
return element.classList.add('failed');
|
|
616
|
+
return this.markMessageFailedFromResponse(response, element);
|
|
563
617
|
}
|
|
564
618
|
var data = yield response.json();
|
|
565
619
|
element.setAttribute('data-id', data.id);
|
|
620
|
+
this.localizeMessageTimestamp(element.querySelector('[data-message-timestamp]'), data.created_at || data.createdAt);
|
|
566
621
|
this.clearRevealedOpeningSequenceMessageIds();
|
|
567
622
|
Hellotext.eventEmitter.dispatch('webchat:message:sent', {
|
|
568
623
|
id: data.id,
|
|
@@ -664,15 +719,12 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
664
719
|
if (response.failed) {
|
|
665
720
|
// Clear the optimistic typing indicator on failure
|
|
666
721
|
clearTimeout(this.optimisticTypingTimeout);
|
|
667
|
-
this.
|
|
668
|
-
type: 'message:failed',
|
|
669
|
-
id: element.id
|
|
670
|
-
});
|
|
671
|
-
return element.classList.add('failed');
|
|
722
|
+
return this.markMessageFailedFromResponse(response, element);
|
|
672
723
|
}
|
|
673
724
|
var data = yield response.json();
|
|
674
725
|
element.setAttribute('data-id', data.id);
|
|
675
726
|
message.id = data.id;
|
|
727
|
+
this.localizeMessageTimestamp(element.querySelector('[data-message-timestamp]'), data.created_at || data.createdAt);
|
|
676
728
|
this.clearRevealedOpeningSequenceMessageIds();
|
|
677
729
|
Hellotext.eventEmitter.dispatch('webchat:message:sent', message);
|
|
678
730
|
if (data.conversation !== this.conversationIdValue) {
|
|
@@ -700,8 +752,145 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
700
752
|
element.style.removeProperty('display');
|
|
701
753
|
element.setAttribute('data-controller', 'hellotext--message');
|
|
702
754
|
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
755
|
+
this.localizeMessageTimestamp(element.querySelector('[data-message-timestamp]'), new Date());
|
|
703
756
|
return element;
|
|
704
757
|
}
|
|
758
|
+
}, {
|
|
759
|
+
key: "focusCompose",
|
|
760
|
+
value: function focusCompose(event) {
|
|
761
|
+
var {
|
|
762
|
+
target
|
|
763
|
+
} = event;
|
|
764
|
+
var ignoredSelector = ['button', 'a', 'input', 'textarea', 'select', 'label', '[role="button"]', 'em-emoji-picker', '[data-hellotext--webchat--emoji-target~="popover"]', '[data-controller~="hellotext--webchat--emoji"]'].join(', ');
|
|
765
|
+
if (!this.hasInputTarget || target.closest(ignoredSelector)) return;
|
|
766
|
+
event.preventDefault();
|
|
767
|
+
this.inputTarget.focus();
|
|
768
|
+
if (typeof this.inputTarget.selectionStart === 'number') {
|
|
769
|
+
var position = this.inputTarget.value.length;
|
|
770
|
+
this.inputTarget.setSelectionRange(position, position);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}, {
|
|
774
|
+
key: "closePopoverFromHeader",
|
|
775
|
+
value: function closePopoverFromHeader(event) {
|
|
776
|
+
var {
|
|
777
|
+
target
|
|
778
|
+
} = event;
|
|
779
|
+
if (target.closest('.hellotext--webchat-header-channel-button, .hellotext--webchat-close-button')) return;
|
|
780
|
+
event.preventDefault();
|
|
781
|
+
this.closePopover();
|
|
782
|
+
}
|
|
783
|
+
}, {
|
|
784
|
+
key: "closePopoverOnEscape",
|
|
785
|
+
value: function closePopoverOnEscape(event) {
|
|
786
|
+
var _this$triggerTarget, _this$triggerTarget$f;
|
|
787
|
+
if (event.key !== 'Escape' || !this.openValue) return;
|
|
788
|
+
event.preventDefault();
|
|
789
|
+
event.stopPropagation();
|
|
790
|
+
this.closePopover();
|
|
791
|
+
(_this$triggerTarget = this.triggerTarget) === null || _this$triggerTarget === void 0 ? void 0 : (_this$triggerTarget$f = _this$triggerTarget.focus) === null || _this$triggerTarget$f === void 0 ? void 0 : _this$triggerTarget$f.call(_this$triggerTarget);
|
|
792
|
+
}
|
|
793
|
+
}, {
|
|
794
|
+
key: "markMessageFailedFromResponse",
|
|
795
|
+
value: function () {
|
|
796
|
+
var _markMessageFailedFromResponse = _asyncToGenerator(function* (response, element) {
|
|
797
|
+
var reason = yield this.messageFailureReason(response);
|
|
798
|
+
this.markMessageFailed(element, reason);
|
|
799
|
+
this.broadcastChannel.postMessage({
|
|
800
|
+
type: 'message:failed',
|
|
801
|
+
id: element.id,
|
|
802
|
+
reason
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
function markMessageFailedFromResponse(_x4, _x5) {
|
|
806
|
+
return _markMessageFailedFromResponse.apply(this, arguments);
|
|
807
|
+
}
|
|
808
|
+
return markMessageFailedFromResponse;
|
|
809
|
+
}()
|
|
810
|
+
}, {
|
|
811
|
+
key: "markMessageFailed",
|
|
812
|
+
value: function markMessageFailed(element, reason) {
|
|
813
|
+
if (!element) return;
|
|
814
|
+
element.classList.add('failed');
|
|
815
|
+
if (!reason) return;
|
|
816
|
+
var timestamp = element.querySelector('[data-message-timestamp]');
|
|
817
|
+
if (timestamp) {
|
|
818
|
+
timestamp.textContent = reason;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}, {
|
|
822
|
+
key: "localizeMessageTimestamps",
|
|
823
|
+
value: function localizeMessageTimestamps() {
|
|
824
|
+
var _root$matches, _root$querySelectorAl;
|
|
825
|
+
var root = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.element;
|
|
826
|
+
if (!root) return;
|
|
827
|
+
var timestamps = (_root$matches = root.matches) !== null && _root$matches !== void 0 && _root$matches.call(root, 'time[datetime][data-message-timestamp]') ? [root] : Array.from(((_root$querySelectorAl = root.querySelectorAll) === null || _root$querySelectorAl === void 0 ? void 0 : _root$querySelectorAl.call(root, 'time[datetime][data-message-timestamp]')) || []);
|
|
828
|
+
timestamps.forEach(timestamp => this.localizeMessageTimestamp(timestamp));
|
|
829
|
+
}
|
|
830
|
+
}, {
|
|
831
|
+
key: "localizeMessageTimestamp",
|
|
832
|
+
value: function localizeMessageTimestamp(timestamp) {
|
|
833
|
+
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : timestamp === null || timestamp === void 0 ? void 0 : timestamp.getAttribute('datetime');
|
|
834
|
+
if (!timestamp || !value) return;
|
|
835
|
+
var date = value instanceof Date ? value : new Date(value);
|
|
836
|
+
if (Number.isNaN(date.getTime())) return;
|
|
837
|
+
timestamp.setAttribute('datetime', date.toISOString());
|
|
838
|
+
timestamp.textContent = this.formatMessageTimestamp(date);
|
|
839
|
+
}
|
|
840
|
+
}, {
|
|
841
|
+
key: "formatMessageTimestamp",
|
|
842
|
+
value: function formatMessageTimestamp(date) {
|
|
843
|
+
return this.constructor.messageTimestampFormatterFor(Locale.toString()).format(date);
|
|
844
|
+
}
|
|
845
|
+
}, {
|
|
846
|
+
key: "messageFailureReason",
|
|
847
|
+
value: function () {
|
|
848
|
+
var _messageFailureReason = _asyncToGenerator(function* (response) {
|
|
849
|
+
var nativeResponse = (response === null || response === void 0 ? void 0 : response.data) || (response === null || response === void 0 ? void 0 : response.response);
|
|
850
|
+
var fallback = (nativeResponse === null || nativeResponse === void 0 ? void 0 : nativeResponse.statusText) || 'Message failed';
|
|
851
|
+
try {
|
|
852
|
+
var _jsonResponse$json;
|
|
853
|
+
var jsonResponse = nativeResponse !== null && nativeResponse !== void 0 && nativeResponse.clone ? nativeResponse.clone() : nativeResponse;
|
|
854
|
+
var payload = yield jsonResponse === null || jsonResponse === void 0 ? void 0 : (_jsonResponse$json = jsonResponse.json) === null || _jsonResponse$json === void 0 ? void 0 : _jsonResponse$json.call(jsonResponse);
|
|
855
|
+
var reason = this.messageFailureReasonFromPayload(payload);
|
|
856
|
+
if (reason) return reason;
|
|
857
|
+
} catch (_) {
|
|
858
|
+
// Fall through to text parsing/fallback. Some stores strip content-type or
|
|
859
|
+
// return non-JSON failures, so the timestamp still needs a useful reason.
|
|
860
|
+
}
|
|
861
|
+
try {
|
|
862
|
+
var _textResponse$text;
|
|
863
|
+
var textResponse = nativeResponse !== null && nativeResponse !== void 0 && nativeResponse.clone ? nativeResponse.clone() : nativeResponse;
|
|
864
|
+
var text = yield textResponse === null || textResponse === void 0 ? void 0 : (_textResponse$text = textResponse.text) === null || _textResponse$text === void 0 ? void 0 : _textResponse$text.call(textResponse);
|
|
865
|
+
return this.messageFailureReasonFromText(text) || fallback;
|
|
866
|
+
} catch (_) {
|
|
867
|
+
return fallback;
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
function messageFailureReason(_x6) {
|
|
871
|
+
return _messageFailureReason.apply(this, arguments);
|
|
872
|
+
}
|
|
873
|
+
return messageFailureReason;
|
|
874
|
+
}()
|
|
875
|
+
}, {
|
|
876
|
+
key: "messageFailureReasonFromText",
|
|
877
|
+
value: function messageFailureReasonFromText(text) {
|
|
878
|
+
if (typeof text !== 'string') return null;
|
|
879
|
+
var value = text.trim();
|
|
880
|
+
if (!value || value.startsWith('<')) return null;
|
|
881
|
+
try {
|
|
882
|
+
return this.messageFailureReasonFromPayload(JSON.parse(value)) || value;
|
|
883
|
+
} catch (_) {
|
|
884
|
+
return value;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}, {
|
|
888
|
+
key: "messageFailureReasonFromPayload",
|
|
889
|
+
value: function messageFailureReasonFromPayload(payload) {
|
|
890
|
+
var _payload$error, _payload$errors, _payload$errors2, _payload$errors2$, _payload$errors3, _payload$errors3$;
|
|
891
|
+
if (!payload) return null;
|
|
892
|
+
return [(_payload$error = payload.error) === null || _payload$error === void 0 ? void 0 : _payload$error.message, payload.message, (_payload$errors = payload.errors) === null || _payload$errors === void 0 ? void 0 : _payload$errors.message, (_payload$errors2 = payload.errors) === null || _payload$errors2 === void 0 ? void 0 : (_payload$errors2$ = _payload$errors2[0]) === null || _payload$errors2$ === void 0 ? void 0 : _payload$errors2$.message, (_payload$errors3 = payload.errors) === null || _payload$errors3 === void 0 ? void 0 : (_payload$errors3$ = _payload$errors3[0]) === null || _payload$errors3$ === void 0 ? void 0 : _payload$errors3$.description].find(reason => typeof reason === 'string' && reason.trim().length > 0);
|
|
893
|
+
}
|
|
705
894
|
}, {
|
|
706
895
|
key: "messageAttachmentsContainer",
|
|
707
896
|
value: function messageAttachmentsContainer(element) {
|
|
@@ -832,9 +1021,28 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
832
1021
|
get: function get() {
|
|
833
1022
|
return window.matchMedia("(max-width: ".concat(this.fullScreenThresholdValue, "px)")).matches;
|
|
834
1023
|
}
|
|
1024
|
+
}], [{
|
|
1025
|
+
key: "messageTimestampFormatterFor",
|
|
1026
|
+
value: function messageTimestampFormatterFor(locale) {
|
|
1027
|
+
var key = locale || 'default';
|
|
1028
|
+
if (!this.messageTimestampFormatters[key]) {
|
|
1029
|
+
this.messageTimestampFormatters[key] = this.buildMessageTimestampFormatter(locale);
|
|
1030
|
+
}
|
|
1031
|
+
return this.messageTimestampFormatters[key];
|
|
1032
|
+
}
|
|
1033
|
+
}, {
|
|
1034
|
+
key: "buildMessageTimestampFormatter",
|
|
1035
|
+
value: function buildMessageTimestampFormatter(locale) {
|
|
1036
|
+
try {
|
|
1037
|
+
return new Intl.DateTimeFormat(locale || undefined, MESSAGE_TIMESTAMP_FORMAT_OPTIONS);
|
|
1038
|
+
} catch (_) {
|
|
1039
|
+
return new Intl.DateTimeFormat(undefined, MESSAGE_TIMESTAMP_FORMAT_OPTIONS);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
835
1042
|
}]);
|
|
836
1043
|
return _default;
|
|
837
1044
|
}(Controller);
|
|
1045
|
+
_default.messageTimestampFormatters = {};
|
|
838
1046
|
_default.values = {
|
|
839
1047
|
id: String,
|
|
840
1048
|
conversationId: String,
|
|
@@ -39,6 +39,7 @@ let Configuration = /*#__PURE__*/function () {
|
|
|
39
39
|
*/
|
|
40
40
|
function assign(props) {
|
|
41
41
|
if (props) {
|
|
42
|
+
const shouldInferActionCableUrl = Object.prototype.hasOwnProperty.call(props, 'apiRoot') && !Object.prototype.hasOwnProperty.call(props, 'actionCableUrl');
|
|
42
43
|
Object.entries(props).forEach(([key, value]) => {
|
|
43
44
|
if (key === 'forms') {
|
|
44
45
|
this.forms = _forms.Forms.assign(value);
|
|
@@ -48,6 +49,9 @@ let Configuration = /*#__PURE__*/function () {
|
|
|
48
49
|
this[key] = value;
|
|
49
50
|
}
|
|
50
51
|
});
|
|
52
|
+
if (shouldInferActionCableUrl) {
|
|
53
|
+
this.actionCableUrl = this.actionCableUrlForApiRoot(this.apiRoot);
|
|
54
|
+
}
|
|
51
55
|
}
|
|
52
56
|
return this;
|
|
53
57
|
}
|
|
@@ -64,6 +68,21 @@ let Configuration = /*#__PURE__*/function () {
|
|
|
64
68
|
value: function endpoint(path) {
|
|
65
69
|
return `${this.apiRoot}/${path}`;
|
|
66
70
|
}
|
|
71
|
+
}, {
|
|
72
|
+
key: "actionCableUrlForApiRoot",
|
|
73
|
+
value: function actionCableUrlForApiRoot(apiRoot) {
|
|
74
|
+
try {
|
|
75
|
+
const url = new URL(apiRoot);
|
|
76
|
+
const protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
77
|
+
url.protocol = protocol;
|
|
78
|
+
url.pathname = '/cable';
|
|
79
|
+
url.search = '';
|
|
80
|
+
url.hash = '';
|
|
81
|
+
return url.toString();
|
|
82
|
+
} catch (_) {
|
|
83
|
+
return this.actionCableUrl;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
67
86
|
}]);
|
|
68
87
|
return Configuration;
|
|
69
88
|
}();
|
|
@@ -34,6 +34,7 @@ var Configuration = /*#__PURE__*/function () {
|
|
|
34
34
|
*/
|
|
35
35
|
function assign(props) {
|
|
36
36
|
if (props) {
|
|
37
|
+
var shouldInferActionCableUrl = Object.prototype.hasOwnProperty.call(props, 'apiRoot') && !Object.prototype.hasOwnProperty.call(props, 'actionCableUrl');
|
|
37
38
|
Object.entries(props).forEach(_ref => {
|
|
38
39
|
var [key, value] = _ref;
|
|
39
40
|
if (key === 'forms') {
|
|
@@ -44,6 +45,9 @@ var Configuration = /*#__PURE__*/function () {
|
|
|
44
45
|
this[key] = value;
|
|
45
46
|
}
|
|
46
47
|
});
|
|
48
|
+
if (shouldInferActionCableUrl) {
|
|
49
|
+
this.actionCableUrl = this.actionCableUrlForApiRoot(this.apiRoot);
|
|
50
|
+
}
|
|
47
51
|
}
|
|
48
52
|
return this;
|
|
49
53
|
}
|
|
@@ -60,6 +64,21 @@ var Configuration = /*#__PURE__*/function () {
|
|
|
60
64
|
value: function endpoint(path) {
|
|
61
65
|
return "".concat(this.apiRoot, "/").concat(path);
|
|
62
66
|
}
|
|
67
|
+
}, {
|
|
68
|
+
key: "actionCableUrlForApiRoot",
|
|
69
|
+
value: function actionCableUrlForApiRoot(apiRoot) {
|
|
70
|
+
try {
|
|
71
|
+
var url = new URL(apiRoot);
|
|
72
|
+
var protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
73
|
+
url.protocol = protocol;
|
|
74
|
+
url.pathname = '/cable';
|
|
75
|
+
url.search = '';
|
|
76
|
+
url.hash = '';
|
|
77
|
+
return url.toString();
|
|
78
|
+
} catch (_) {
|
|
79
|
+
return this.actionCableUrl;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
63
82
|
}]);
|
|
64
83
|
return Configuration;
|
|
65
84
|
}();
|
package/lib/models/business.cjs
CHANGED
|
@@ -12,17 +12,22 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+
|
|
|
12
12
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
13
13
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
14
14
|
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
15
|
+
const stylesheetAttribute = 'data-hellotext-stylesheet';
|
|
16
|
+
const stylesheetLoadTimeout = 10000;
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* @typedef {Object} BusinessCountry
|
|
17
20
|
* @property {String} [code] - ISO country code configured for the business.
|
|
18
21
|
* @property {String} [prefix] - Phone country prefix configured for the business.
|
|
19
22
|
*/
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* @typedef {Object} BusinessWebchat
|
|
22
26
|
* @property {String} [id] - Dashboard webchat id configured for the business.
|
|
23
27
|
* @property {Object} [appearance] - Dashboard appearance defaults for the webchat.
|
|
24
28
|
* @property {Object} [whatsapp] - Dashboard WhatsApp handoff defaults for the webchat.
|
|
25
29
|
*/
|
|
30
|
+
|
|
26
31
|
/**
|
|
27
32
|
* Public business metadata returned by `public/businesses/:id`.
|
|
28
33
|
*
|
|
@@ -36,6 +41,7 @@ function _toPrimitive(input, hint) { if (typeof input !== "object" || input ===
|
|
|
36
41
|
* @property {String|Array<String>} [whitelist] - Domain whitelist configuration.
|
|
37
42
|
* @property {String} [subscription] - Current business subscription tier.
|
|
38
43
|
*/
|
|
44
|
+
|
|
39
45
|
/**
|
|
40
46
|
* Public business context used by the SDK for tracking, forms, and webchat defaults.
|
|
41
47
|
*/
|
|
@@ -47,6 +53,8 @@ let Business = /*#__PURE__*/function () {
|
|
|
47
53
|
_classCallCheck(this, Business);
|
|
48
54
|
this.id = id;
|
|
49
55
|
this.data = null;
|
|
56
|
+
this.stylesheet = null;
|
|
57
|
+
this.stylesheetLoaded = Promise.resolve(false);
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/**
|
|
@@ -87,11 +95,12 @@ let Business = /*#__PURE__*/function () {
|
|
|
87
95
|
key: "setData",
|
|
88
96
|
value: function setData(data) {
|
|
89
97
|
this.data = data;
|
|
90
|
-
if (typeof document !== 'undefined') {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
if (typeof document !== 'undefined' && data.style_url) {
|
|
99
|
+
this.stylesheet = this.constructor.ensureStylesheet(data.style_url);
|
|
100
|
+
this.stylesheetLoaded = this.constructor.waitForStylesheet(this.stylesheet);
|
|
101
|
+
} else {
|
|
102
|
+
this.stylesheet = null;
|
|
103
|
+
this.stylesheetLoaded = Promise.resolve(false);
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
}, {
|
|
@@ -135,6 +144,78 @@ let Business = /*#__PURE__*/function () {
|
|
|
135
144
|
get: function () {
|
|
136
145
|
return this.data.features;
|
|
137
146
|
}
|
|
147
|
+
}], [{
|
|
148
|
+
key: "stylesheetSelector",
|
|
149
|
+
get: function () {
|
|
150
|
+
return `link[rel="stylesheet"][${stylesheetAttribute}]`;
|
|
151
|
+
}
|
|
152
|
+
}, {
|
|
153
|
+
key: "ensureStylesheet",
|
|
154
|
+
value: function ensureStylesheet(styleUrl) {
|
|
155
|
+
const href = this.normalizedStylesheetUrl(styleUrl);
|
|
156
|
+
const existingLink = this.stylesheetLinks.find(link => link.href === href);
|
|
157
|
+
if (existingLink) {
|
|
158
|
+
existingLink.setAttribute(stylesheetAttribute, 'true');
|
|
159
|
+
return existingLink;
|
|
160
|
+
}
|
|
161
|
+
const linkTag = document.createElement('link');
|
|
162
|
+
linkTag.rel = 'stylesheet';
|
|
163
|
+
linkTag.href = styleUrl;
|
|
164
|
+
linkTag.setAttribute(stylesheetAttribute, 'true');
|
|
165
|
+
this.waitForStylesheet(linkTag);
|
|
166
|
+
document.head.append(linkTag);
|
|
167
|
+
return linkTag;
|
|
168
|
+
}
|
|
169
|
+
}, {
|
|
170
|
+
key: "stylesheetLinks",
|
|
171
|
+
get: function () {
|
|
172
|
+
if (typeof document === 'undefined') return [];
|
|
173
|
+
return Array.from(document.querySelectorAll(this.stylesheetSelector));
|
|
174
|
+
}
|
|
175
|
+
}, {
|
|
176
|
+
key: "latestStylesheet",
|
|
177
|
+
get: function () {
|
|
178
|
+
return this.stylesheetLinks[this.stylesheetLinks.length - 1];
|
|
179
|
+
}
|
|
180
|
+
}, {
|
|
181
|
+
key: "normalizedStylesheetUrl",
|
|
182
|
+
value: function normalizedStylesheetUrl(styleUrl) {
|
|
183
|
+
try {
|
|
184
|
+
return new URL(styleUrl, document.baseURI).href;
|
|
185
|
+
} catch (_error) {
|
|
186
|
+
return styleUrl;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}, {
|
|
190
|
+
key: "waitForStylesheet",
|
|
191
|
+
value: function waitForStylesheet(linkTag) {
|
|
192
|
+
if (!linkTag) return Promise.resolve(false);
|
|
193
|
+
if (this.stylesheetIsLoaded(linkTag)) return Promise.resolve(true);
|
|
194
|
+
if (linkTag.dataset.hellotextStylesheetLoaded === 'false') return Promise.resolve(false);
|
|
195
|
+
if (linkTag._hellotextStylesheetLoaded) return linkTag._hellotextStylesheetLoaded;
|
|
196
|
+
linkTag._hellotextStylesheetLoaded = new Promise(resolve => {
|
|
197
|
+
let timeout;
|
|
198
|
+
const finish = loaded => {
|
|
199
|
+
clearTimeout(timeout);
|
|
200
|
+
linkTag.removeEventListener('load', handleLoad);
|
|
201
|
+
linkTag.removeEventListener('error', handleError);
|
|
202
|
+
linkTag.dataset.hellotextStylesheetLoaded = loaded ? 'true' : 'false';
|
|
203
|
+
resolve(loaded);
|
|
204
|
+
};
|
|
205
|
+
const handleLoad = () => finish(this.stylesheetIsLoaded(linkTag));
|
|
206
|
+
const handleError = () => finish(false);
|
|
207
|
+
linkTag.addEventListener('load', handleLoad);
|
|
208
|
+
linkTag.addEventListener('error', handleError);
|
|
209
|
+
timeout = setTimeout(() => finish(this.stylesheetIsLoaded(linkTag)), stylesheetLoadTimeout);
|
|
210
|
+
if (timeout.unref) timeout.unref();
|
|
211
|
+
});
|
|
212
|
+
return linkTag._hellotextStylesheetLoaded;
|
|
213
|
+
}
|
|
214
|
+
}, {
|
|
215
|
+
key: "stylesheetIsLoaded",
|
|
216
|
+
value: function stylesheetIsLoaded(linkTag) {
|
|
217
|
+
return linkTag.dataset.hellotextStylesheetLoaded === 'true' || !!linkTag.sheet;
|
|
218
|
+
}
|
|
138
219
|
}]);
|
|
139
220
|
return Business;
|
|
140
221
|
}();
|