@hellotext/hellotext 2.0.9 → 2.1.1
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/index.d.ts +1 -0
- package/lib/controllers/mixins/usePopover.cjs +9 -7
- package/lib/controllers/mixins/usePopover.js +9 -6
- package/lib/controllers/webchat_controller.cjs +39 -0
- package/lib/controllers/webchat_controller.js +39 -0
- package/lib/core/configuration/webchat.cjs +31 -1
- package/lib/core/configuration/webchat.js +31 -1
- package/package.json +1 -1
- package/src/controllers/mixins/usePopover.js +14 -12
- package/src/controllers/webchat_controller.js +43 -0
- package/src/core/configuration/webchat.js +53 -20
package/index.d.ts
CHANGED
|
@@ -5,8 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.usePopover = void 0;
|
|
7
7
|
var _dom = require("@floating-ui/dom");
|
|
8
|
-
var
|
|
9
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
8
|
+
var _core = require("../../core");
|
|
10
9
|
const usePopover = controller => {
|
|
11
10
|
Object.assign(controller, {
|
|
12
11
|
show() {
|
|
@@ -25,14 +24,17 @@ const usePopover = controller => {
|
|
|
25
24
|
this.floatingUICleanup = (0, _dom.autoUpdate)(trigger, popover, () => {
|
|
26
25
|
(0, _dom.computePosition)(trigger, popover, {
|
|
27
26
|
placement: this.placementValue,
|
|
28
|
-
middleware: this.middlewares
|
|
27
|
+
middleware: this.middlewares,
|
|
28
|
+
strategy: _core.Configuration.webchat.strategy
|
|
29
29
|
}).then(({
|
|
30
30
|
x,
|
|
31
|
-
y
|
|
31
|
+
y,
|
|
32
|
+
strategy
|
|
32
33
|
}) => {
|
|
33
34
|
const newStyle = {
|
|
34
35
|
left: `${x}px`,
|
|
35
|
-
top: `${y}px
|
|
36
|
+
top: `${y}px`,
|
|
37
|
+
position: strategy
|
|
36
38
|
};
|
|
37
39
|
Object.assign(popover.style, newStyle);
|
|
38
40
|
});
|
|
@@ -42,13 +44,13 @@ const usePopover = controller => {
|
|
|
42
44
|
if (this.disabledValue) return;
|
|
43
45
|
if (this.openValue) {
|
|
44
46
|
this.popoverTarget.showPopover();
|
|
45
|
-
this.popoverTarget.setAttribute(
|
|
47
|
+
this.popoverTarget.setAttribute('aria-expanded', 'true');
|
|
46
48
|
if (this['onPopoverOpened']) {
|
|
47
49
|
this.onPopoverOpened();
|
|
48
50
|
}
|
|
49
51
|
} else {
|
|
50
52
|
this.popoverTarget.hidePopover();
|
|
51
|
-
this.popoverTarget.removeAttribute(
|
|
53
|
+
this.popoverTarget.removeAttribute('aria-expanded');
|
|
52
54
|
if (this['onPopoverClosed']) {
|
|
53
55
|
this.onPopoverClosed();
|
|
54
56
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { autoUpdate, computePosition } from '@floating-ui/dom';
|
|
2
|
-
import
|
|
2
|
+
import { Configuration } from '../../core';
|
|
3
3
|
export var usePopover = controller => {
|
|
4
4
|
Object.assign(controller, {
|
|
5
5
|
show() {
|
|
@@ -19,15 +19,18 @@ export var usePopover = controller => {
|
|
|
19
19
|
this.floatingUICleanup = autoUpdate(trigger, popover, () => {
|
|
20
20
|
computePosition(trigger, popover, {
|
|
21
21
|
placement: this.placementValue,
|
|
22
|
-
middleware: this.middlewares
|
|
22
|
+
middleware: this.middlewares,
|
|
23
|
+
strategy: Configuration.webchat.strategy
|
|
23
24
|
}).then(_ref2 => {
|
|
24
25
|
var {
|
|
25
26
|
x,
|
|
26
|
-
y
|
|
27
|
+
y,
|
|
28
|
+
strategy
|
|
27
29
|
} = _ref2;
|
|
28
30
|
var newStyle = {
|
|
29
31
|
left: "".concat(x, "px"),
|
|
30
|
-
top: "".concat(y, "px")
|
|
32
|
+
top: "".concat(y, "px"),
|
|
33
|
+
position: strategy
|
|
31
34
|
};
|
|
32
35
|
Object.assign(popover.style, newStyle);
|
|
33
36
|
});
|
|
@@ -37,13 +40,13 @@ export var usePopover = controller => {
|
|
|
37
40
|
if (this.disabledValue) return;
|
|
38
41
|
if (this.openValue) {
|
|
39
42
|
this.popoverTarget.showPopover();
|
|
40
|
-
this.popoverTarget.setAttribute(
|
|
43
|
+
this.popoverTarget.setAttribute('aria-expanded', 'true');
|
|
41
44
|
if (this['onPopoverOpened']) {
|
|
42
45
|
this.onPopoverOpened();
|
|
43
46
|
}
|
|
44
47
|
} else {
|
|
45
48
|
this.popoverTarget.hidePopover();
|
|
46
|
-
this.popoverTarget.removeAttribute(
|
|
49
|
+
this.popoverTarget.removeAttribute('aria-expanded');
|
|
47
50
|
if (this['onPopoverClosed']) {
|
|
48
51
|
this.onPopoverClosed();
|
|
49
52
|
}
|
|
@@ -45,6 +45,8 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
45
45
|
this.onAgentOnline = this.onAgentOnline.bind(this);
|
|
46
46
|
this.onMessageReaction = this.onMessageReaction.bind(this);
|
|
47
47
|
this.onScroll = this.onScroll.bind(this);
|
|
48
|
+
this.onOutboundMessageSent = this.onOutboundMessageSent.bind(this);
|
|
49
|
+
this.broadcastChannel = new BroadcastChannel(`hellotext--webchat--${this.idValue}`);
|
|
48
50
|
_get(_getPrototypeOf(_default.prototype), "initialize", this).call(this);
|
|
49
51
|
}
|
|
50
52
|
}, {
|
|
@@ -69,15 +71,43 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
69
71
|
this.openValue = true;
|
|
70
72
|
}
|
|
71
73
|
_hellotext.default.eventEmitter.dispatch('webchat:mounted');
|
|
74
|
+
this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent);
|
|
72
75
|
_get(_getPrototypeOf(_default.prototype), "connect", this).call(this);
|
|
73
76
|
}
|
|
74
77
|
}, {
|
|
75
78
|
key: "disconnect",
|
|
76
79
|
value: function disconnect() {
|
|
80
|
+
this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
|
|
77
81
|
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
|
|
82
|
+
this.broadcastChannel.close();
|
|
78
83
|
this.floatingUICleanup();
|
|
79
84
|
_get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this);
|
|
80
85
|
}
|
|
86
|
+
}, {
|
|
87
|
+
key: "onOutboundMessageSent",
|
|
88
|
+
value: function onOutboundMessageSent(event) {
|
|
89
|
+
const {
|
|
90
|
+
data
|
|
91
|
+
} = event;
|
|
92
|
+
const callbacks = {
|
|
93
|
+
'message:sent': data => {
|
|
94
|
+
const element = new DOMParser().parseFromString(data.element, 'text/html').body.firstElementChild;
|
|
95
|
+
this.messagesContainerTarget.appendChild(element);
|
|
96
|
+
element.scrollIntoView({
|
|
97
|
+
behavior: 'instant'
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
'message:failed': data => {
|
|
101
|
+
var _this$messagesContain;
|
|
102
|
+
(_this$messagesContain = this.messagesContainerTarget.querySelector(`#${data.id}`)) === null || _this$messagesContain === void 0 ? void 0 : _this$messagesContain.classList.add('failed');
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
if (callbacks[data.type]) {
|
|
106
|
+
callbacks[data.type](data);
|
|
107
|
+
} else {
|
|
108
|
+
console.log(`Unhandled message event: ${data.type}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
81
111
|
}, {
|
|
82
112
|
key: "onScroll",
|
|
83
113
|
value: async function onScroll() {
|
|
@@ -253,6 +283,7 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
253
283
|
});
|
|
254
284
|
formData.append('session', _hellotext.default.session);
|
|
255
285
|
const element = this.messageTemplateTarget.cloneNode(true);
|
|
286
|
+
element.id = `hellotext--webchat--${this.idValue}--message--${Date.now()}`;
|
|
256
287
|
element.classList.add('received');
|
|
257
288
|
element.style.removeProperty('display');
|
|
258
289
|
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
@@ -267,6 +298,10 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
267
298
|
element.scrollIntoView({
|
|
268
299
|
behavior: 'smooth'
|
|
269
300
|
});
|
|
301
|
+
this.broadcastChannel.postMessage({
|
|
302
|
+
type: 'message:sent',
|
|
303
|
+
element: element.outerHTML
|
|
304
|
+
});
|
|
270
305
|
this.inputTarget.value = '';
|
|
271
306
|
this.files = [];
|
|
272
307
|
this.attachmentContainerTarget.style.display = 'none';
|
|
@@ -274,6 +309,10 @@ let _default = /*#__PURE__*/function (_Controller) {
|
|
|
274
309
|
this.inputTarget.focus();
|
|
275
310
|
const response = await this.messagesAPI.create(formData);
|
|
276
311
|
if (response.failed) {
|
|
312
|
+
this.broadcastChannel.postMessage({
|
|
313
|
+
type: 'message:failed',
|
|
314
|
+
id: element.id
|
|
315
|
+
});
|
|
277
316
|
return element.classList.add('failed');
|
|
278
317
|
}
|
|
279
318
|
const data = await response.json();
|
|
@@ -43,6 +43,8 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
43
43
|
this.onAgentOnline = this.onAgentOnline.bind(this);
|
|
44
44
|
this.onMessageReaction = this.onMessageReaction.bind(this);
|
|
45
45
|
this.onScroll = this.onScroll.bind(this);
|
|
46
|
+
this.onOutboundMessageSent = this.onOutboundMessageSent.bind(this);
|
|
47
|
+
this.broadcastChannel = new BroadcastChannel("hellotext--webchat--".concat(this.idValue));
|
|
46
48
|
_get(_getPrototypeOf(_default.prototype), "initialize", this).call(this);
|
|
47
49
|
}
|
|
48
50
|
}, {
|
|
@@ -67,15 +69,43 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
67
69
|
this.openValue = true;
|
|
68
70
|
}
|
|
69
71
|
Hellotext.eventEmitter.dispatch('webchat:mounted');
|
|
72
|
+
this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent);
|
|
70
73
|
_get(_getPrototypeOf(_default.prototype), "connect", this).call(this);
|
|
71
74
|
}
|
|
72
75
|
}, {
|
|
73
76
|
key: "disconnect",
|
|
74
77
|
value: function disconnect() {
|
|
78
|
+
this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
|
|
75
79
|
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
|
|
80
|
+
this.broadcastChannel.close();
|
|
76
81
|
this.floatingUICleanup();
|
|
77
82
|
_get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this);
|
|
78
83
|
}
|
|
84
|
+
}, {
|
|
85
|
+
key: "onOutboundMessageSent",
|
|
86
|
+
value: function onOutboundMessageSent(event) {
|
|
87
|
+
var {
|
|
88
|
+
data
|
|
89
|
+
} = event;
|
|
90
|
+
var callbacks = {
|
|
91
|
+
'message:sent': data => {
|
|
92
|
+
var element = new DOMParser().parseFromString(data.element, 'text/html').body.firstElementChild;
|
|
93
|
+
this.messagesContainerTarget.appendChild(element);
|
|
94
|
+
element.scrollIntoView({
|
|
95
|
+
behavior: 'instant'
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
'message:failed': data => {
|
|
99
|
+
var _this$messagesContain;
|
|
100
|
+
(_this$messagesContain = this.messagesContainerTarget.querySelector("#".concat(data.id))) === null || _this$messagesContain === void 0 ? void 0 : _this$messagesContain.classList.add('failed');
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
if (callbacks[data.type]) {
|
|
104
|
+
callbacks[data.type](data);
|
|
105
|
+
} else {
|
|
106
|
+
console.log("Unhandled message event: ".concat(data.type));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
79
109
|
}, {
|
|
80
110
|
key: "onScroll",
|
|
81
111
|
value: function () {
|
|
@@ -257,6 +287,7 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
257
287
|
});
|
|
258
288
|
formData.append('session', Hellotext.session);
|
|
259
289
|
var element = this.messageTemplateTarget.cloneNode(true);
|
|
290
|
+
element.id = "hellotext--webchat--".concat(this.idValue, "--message--").concat(Date.now());
|
|
260
291
|
element.classList.add('received');
|
|
261
292
|
element.style.removeProperty('display');
|
|
262
293
|
element.setAttribute('data-hellotext--webchat-target', 'message');
|
|
@@ -271,6 +302,10 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
271
302
|
element.scrollIntoView({
|
|
272
303
|
behavior: 'smooth'
|
|
273
304
|
});
|
|
305
|
+
this.broadcastChannel.postMessage({
|
|
306
|
+
type: 'message:sent',
|
|
307
|
+
element: element.outerHTML
|
|
308
|
+
});
|
|
274
309
|
this.inputTarget.value = '';
|
|
275
310
|
this.files = [];
|
|
276
311
|
this.attachmentContainerTarget.style.display = 'none';
|
|
@@ -278,6 +313,10 @@ var _default = /*#__PURE__*/function (_Controller) {
|
|
|
278
313
|
this.inputTarget.focus();
|
|
279
314
|
var response = yield this.messagesAPI.create(formData);
|
|
280
315
|
if (response.failed) {
|
|
316
|
+
this.broadcastChannel.postMessage({
|
|
317
|
+
type: 'message:failed',
|
|
318
|
+
id: element.id
|
|
319
|
+
});
|
|
281
320
|
return element.classList.add('failed');
|
|
282
321
|
}
|
|
283
322
|
var data = yield response.json();
|
|
@@ -24,6 +24,20 @@ const placements = {
|
|
|
24
24
|
BOTTOM_RIGHT: 'bottom-right'
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {'absolute' | 'fixed'} Strategy
|
|
29
|
+
* @description Valid strategies for the webchat.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @enum {Strategy}
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const strategies = {
|
|
37
|
+
ABSOLUTE: 'absolute',
|
|
38
|
+
FIXED: 'fixed'
|
|
39
|
+
};
|
|
40
|
+
|
|
27
41
|
/**
|
|
28
42
|
* @typedef {'modal' | 'popover'} Behaviour
|
|
29
43
|
*/
|
|
@@ -54,6 +68,7 @@ const behaviors = {
|
|
|
54
68
|
* @property {String} triggerClasses - additional classes to apply to the webchat popup trigger.
|
|
55
69
|
* @property {Behaviour} behaviour - the behaviour of the webchat, defaults to 'popover'.
|
|
56
70
|
* @property {Style} style - the style of the webchat.
|
|
71
|
+
* @property {Strategy} strategy - the strategy used to position the webchat. Defaults to 'absolute'
|
|
57
72
|
*/
|
|
58
73
|
exports.behaviors = behaviors;
|
|
59
74
|
let Webchat = /*#__PURE__*/function () {
|
|
@@ -155,6 +170,20 @@ let Webchat = /*#__PURE__*/function () {
|
|
|
155
170
|
}
|
|
156
171
|
this._behaviour = value;
|
|
157
172
|
}
|
|
173
|
+
}, {
|
|
174
|
+
key: "strategy",
|
|
175
|
+
get: function () {
|
|
176
|
+
if (this._strategy) {
|
|
177
|
+
return this._strategy;
|
|
178
|
+
}
|
|
179
|
+
return this.container == 'body' ? strategies.FIXED : strategies.ABSOLUTE;
|
|
180
|
+
},
|
|
181
|
+
set: function (value) {
|
|
182
|
+
if (value && !Object.values(strategies).includes(value)) {
|
|
183
|
+
throw new Error(`Invalid strategy value: ${value}`);
|
|
184
|
+
}
|
|
185
|
+
this._strategy = value;
|
|
186
|
+
}
|
|
158
187
|
}, {
|
|
159
188
|
key: "assign",
|
|
160
189
|
value: function assign(props) {
|
|
@@ -180,4 +209,5 @@ Webchat._placement = 'bottom-right';
|
|
|
180
209
|
Webchat._classes = [];
|
|
181
210
|
Webchat._triggerClasses = [];
|
|
182
211
|
Webchat._style = {};
|
|
183
|
-
Webchat._behaviour = behaviors.POPOVER;
|
|
212
|
+
Webchat._behaviour = behaviors.POPOVER;
|
|
213
|
+
Webchat._strategy = null;
|
|
@@ -18,6 +18,20 @@ var placements = {
|
|
|
18
18
|
BOTTOM_RIGHT: 'bottom-right'
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {'absolute' | 'fixed'} Strategy
|
|
23
|
+
* @description Valid strategies for the webchat.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @enum {Strategy}
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
var strategies = {
|
|
31
|
+
ABSOLUTE: 'absolute',
|
|
32
|
+
FIXED: 'fixed'
|
|
33
|
+
};
|
|
34
|
+
|
|
21
35
|
/**
|
|
22
36
|
* @typedef {'modal' | 'popover'} Behaviour
|
|
23
37
|
*/
|
|
@@ -48,6 +62,7 @@ var behaviors = {
|
|
|
48
62
|
* @property {String} triggerClasses - additional classes to apply to the webchat popup trigger.
|
|
49
63
|
* @property {Behaviour} behaviour - the behaviour of the webchat, defaults to 'popover'.
|
|
50
64
|
* @property {Style} style - the style of the webchat.
|
|
65
|
+
* @property {Strategy} strategy - the strategy used to position the webchat. Defaults to 'absolute'
|
|
51
66
|
*/
|
|
52
67
|
var Webchat = /*#__PURE__*/function () {
|
|
53
68
|
function Webchat() {
|
|
@@ -149,6 +164,20 @@ var Webchat = /*#__PURE__*/function () {
|
|
|
149
164
|
}
|
|
150
165
|
this._behaviour = value;
|
|
151
166
|
}
|
|
167
|
+
}, {
|
|
168
|
+
key: "strategy",
|
|
169
|
+
get: function get() {
|
|
170
|
+
if (this._strategy) {
|
|
171
|
+
return this._strategy;
|
|
172
|
+
}
|
|
173
|
+
return this.container == 'body' ? strategies.FIXED : strategies.ABSOLUTE;
|
|
174
|
+
},
|
|
175
|
+
set: function set(value) {
|
|
176
|
+
if (value && !Object.values(strategies).includes(value)) {
|
|
177
|
+
throw new Error("Invalid strategy value: ".concat(value));
|
|
178
|
+
}
|
|
179
|
+
this._strategy = value;
|
|
180
|
+
}
|
|
152
181
|
}, {
|
|
153
182
|
key: "assign",
|
|
154
183
|
value: function assign(props) {
|
|
@@ -175,4 +204,5 @@ Webchat._classes = [];
|
|
|
175
204
|
Webchat._triggerClasses = [];
|
|
176
205
|
Webchat._style = {};
|
|
177
206
|
Webchat._behaviour = behaviors.POPOVER;
|
|
178
|
-
|
|
207
|
+
Webchat._strategy = null;
|
|
208
|
+
export { behaviors, Webchat };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { autoUpdate, computePosition } from '@floating-ui/dom'
|
|
2
|
-
import
|
|
2
|
+
import { Configuration } from '../../core'
|
|
3
3
|
|
|
4
|
-
export const usePopover =
|
|
4
|
+
export const usePopover = controller => {
|
|
5
5
|
Object.assign(controller, {
|
|
6
6
|
show() {
|
|
7
7
|
this.openValue = true
|
|
@@ -17,34 +17,36 @@ export const usePopover = (controller) => {
|
|
|
17
17
|
computePosition(trigger, popover, {
|
|
18
18
|
placement: this.placementValue,
|
|
19
19
|
middleware: this.middlewares,
|
|
20
|
-
|
|
20
|
+
strategy: Configuration.webchat.strategy,
|
|
21
|
+
}).then(({ x, y, strategy }) => {
|
|
21
22
|
const newStyle = {
|
|
22
23
|
left: `${x}px`,
|
|
23
|
-
top: `${y}px
|
|
24
|
+
top: `${y}px`,
|
|
25
|
+
position: strategy,
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
Object.assign(popover.style, newStyle)
|
|
27
|
-
})
|
|
29
|
+
})
|
|
28
30
|
})
|
|
29
31
|
},
|
|
30
32
|
openValueChanged() {
|
|
31
|
-
if(this.disabledValue) return
|
|
33
|
+
if (this.disabledValue) return
|
|
32
34
|
|
|
33
|
-
if(this.openValue) {
|
|
35
|
+
if (this.openValue) {
|
|
34
36
|
this.popoverTarget.showPopover()
|
|
35
|
-
this.popoverTarget.setAttribute(
|
|
37
|
+
this.popoverTarget.setAttribute('aria-expanded', 'true')
|
|
36
38
|
|
|
37
|
-
if(this['onPopoverOpened']) {
|
|
39
|
+
if (this['onPopoverOpened']) {
|
|
38
40
|
this.onPopoverOpened()
|
|
39
41
|
}
|
|
40
42
|
} else {
|
|
41
43
|
this.popoverTarget.hidePopover()
|
|
42
|
-
this.popoverTarget.removeAttribute(
|
|
44
|
+
this.popoverTarget.removeAttribute('aria-expanded')
|
|
43
45
|
|
|
44
|
-
if(this['onPopoverClosed']) {
|
|
46
|
+
if (this['onPopoverClosed']) {
|
|
45
47
|
this.onPopoverClosed()
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
}
|
|
50
|
+
},
|
|
49
51
|
})
|
|
50
52
|
}
|
|
@@ -62,6 +62,9 @@ export default class extends Controller {
|
|
|
62
62
|
|
|
63
63
|
this.onScroll = this.onScroll.bind(this)
|
|
64
64
|
|
|
65
|
+
this.onOutboundMessageSent = this.onOutboundMessageSent.bind(this)
|
|
66
|
+
this.broadcastChannel = new BroadcastChannel(`hellotext--webchat--${this.idValue}`)
|
|
67
|
+
|
|
65
68
|
super.initialize()
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -90,16 +93,44 @@ export default class extends Controller {
|
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
Hellotext.eventEmitter.dispatch('webchat:mounted')
|
|
96
|
+
this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent)
|
|
97
|
+
|
|
93
98
|
super.connect()
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
disconnect() {
|
|
102
|
+
this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent)
|
|
97
103
|
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll)
|
|
104
|
+
|
|
105
|
+
this.broadcastChannel.close()
|
|
98
106
|
this.floatingUICleanup()
|
|
99
107
|
|
|
100
108
|
super.disconnect()
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
onOutboundMessageSent(event) {
|
|
112
|
+
const { data } = event
|
|
113
|
+
|
|
114
|
+
const callbacks = {
|
|
115
|
+
'message:sent': data => {
|
|
116
|
+
const element = new DOMParser().parseFromString(data.element, 'text/html').body
|
|
117
|
+
.firstElementChild
|
|
118
|
+
|
|
119
|
+
this.messagesContainerTarget.appendChild(element)
|
|
120
|
+
element.scrollIntoView({ behavior: 'instant' })
|
|
121
|
+
},
|
|
122
|
+
'message:failed': data => {
|
|
123
|
+
this.messagesContainerTarget.querySelector(`#${data.id}`)?.classList.add('failed')
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (callbacks[data.type]) {
|
|
128
|
+
callbacks[data.type](data)
|
|
129
|
+
} else {
|
|
130
|
+
console.log(`Unhandled message event: ${data.type}`)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
103
134
|
async onScroll() {
|
|
104
135
|
if (
|
|
105
136
|
this.messagesContainerTarget.scrollTop > 300 ||
|
|
@@ -307,6 +338,8 @@ export default class extends Controller {
|
|
|
307
338
|
|
|
308
339
|
const element = this.messageTemplateTarget.cloneNode(true)
|
|
309
340
|
|
|
341
|
+
element.id = `hellotext--webchat--${this.idValue}--message--${Date.now()}`
|
|
342
|
+
|
|
310
343
|
element.classList.add('received')
|
|
311
344
|
element.style.removeProperty('display')
|
|
312
345
|
|
|
@@ -324,6 +357,11 @@ export default class extends Controller {
|
|
|
324
357
|
this.messagesContainerTarget.appendChild(element)
|
|
325
358
|
element.scrollIntoView({ behavior: 'smooth' })
|
|
326
359
|
|
|
360
|
+
this.broadcastChannel.postMessage({
|
|
361
|
+
type: 'message:sent',
|
|
362
|
+
element: element.outerHTML,
|
|
363
|
+
})
|
|
364
|
+
|
|
327
365
|
this.inputTarget.value = ''
|
|
328
366
|
this.files = []
|
|
329
367
|
this.attachmentContainerTarget.style.display = 'none'
|
|
@@ -334,6 +372,11 @@ export default class extends Controller {
|
|
|
334
372
|
const response = await this.messagesAPI.create(formData)
|
|
335
373
|
|
|
336
374
|
if (response.failed) {
|
|
375
|
+
this.broadcastChannel.postMessage({
|
|
376
|
+
type: 'message:failed',
|
|
377
|
+
id: element.id,
|
|
378
|
+
})
|
|
379
|
+
|
|
337
380
|
return element.classList.add('failed')
|
|
338
381
|
}
|
|
339
382
|
|