@hellotext/hellotext 2.5.7 → 2.5.8
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 +0 -25
- package/dist/hellotext.js +1 -1
- package/index.d.ts +0 -6
- package/lib/api/acks.cjs +17 -31
- package/lib/api/acks.js +17 -42
- package/lib/api/businesses.cjs +14 -28
- package/lib/api/businesses.js +13 -36
- package/lib/api/events.cjs +26 -40
- package/lib/api/events.js +28 -52
- package/lib/api/forms.cjs +24 -40
- package/lib/api/forms.js +24 -57
- package/lib/api/identifications.cjs +15 -29
- package/lib/api/identifications.js +15 -40
- package/lib/api/index.cjs +25 -51
- package/lib/api/index.js +27 -54
- package/lib/api/push/identities.cjs +30 -46
- package/lib/api/push/identities.js +30 -63
- package/lib/api/response.cjs +30 -52
- package/lib/api/response.js +31 -60
- package/lib/api/submissions.cjs +18 -34
- package/lib/api/submissions.js +18 -48
- package/lib/api/webchat/messages.cjs +44 -64
- package/lib/api/webchat/messages.js +44 -80
- package/lib/api/webchats.cjs +37 -56
- package/lib/api/webchats.js +37 -65
- package/lib/api/whatsapp_widgets.cjs +49 -72
- package/lib/api/whatsapp_widgets.js +48 -91
- package/lib/builders/input_builder.cjs +36 -48
- package/lib/builders/input_builder.js +36 -48
- package/lib/builders/logo_builder.cjs +10 -29
- package/lib/builders/logo_builder.js +20 -28
- package/lib/channels/application_channel.cjs +136 -178
- package/lib/channels/application_channel.js +137 -180
- package/lib/channels/webchat_channel.cjs +125 -170
- package/lib/channels/webchat_channel.js +125 -170
- package/lib/controllers/form_controller.cjs +82 -121
- package/lib/controllers/form_controller.js +82 -130
- package/lib/controllers/message_controller.cjs +211 -282
- package/lib/controllers/message_controller.js +209 -284
- package/lib/controllers/mixins/usePopover.cjs +3 -6
- package/lib/controllers/mixins/usePopover.js +17 -22
- package/lib/controllers/webchat/emoji_picker_controller.cjs +106 -150
- package/lib/controllers/webchat/emoji_picker_controller.js +105 -170
- package/lib/controllers/webchat/useBehaviour.js +5 -5
- package/lib/controllers/webchat/useOpeningSequence.js +6 -6
- package/lib/controllers/webchat/useTeaser.js +9 -9
- package/lib/controllers/webchat_controller.cjs +916 -1088
- package/lib/controllers/webchat_controller.js +926 -1152
- package/lib/core/configuration/forms.cjs +21 -35
- package/lib/core/configuration/forms.js +20 -35
- package/lib/core/configuration/locale.cjs +36 -71
- package/lib/core/configuration/locale.js +36 -70
- package/lib/core/configuration/push.cjs +9 -21
- package/lib/core/configuration/push.js +8 -20
- package/lib/core/configuration/webchat.cjs +142 -182
- package/lib/core/configuration/webchat.js +145 -189
- package/lib/core/configuration/whatsapp.cjs +76 -102
- package/lib/core/configuration/whatsapp.js +76 -105
- package/lib/core/configuration.cjs +60 -78
- package/lib/core/configuration.js +59 -78
- package/lib/core/event.cjs +36 -59
- package/lib/core/event.js +35 -61
- package/lib/core/sanitize_html.js +4 -4
- package/lib/errors/invalid_event.cjs +5 -26
- package/lib/errors/invalid_event.js +5 -26
- package/lib/errors/not_initialized_error.cjs +5 -26
- package/lib/errors/not_initialized_error.js +5 -26
- package/lib/hellotext.cjs +185 -223
- package/lib/hellotext.js +188 -247
- package/lib/index.js +1 -1
- package/lib/models/business.cjs +114 -152
- package/lib/models/business.js +117 -162
- package/lib/models/cookies.cjs +34 -51
- package/lib/models/cookies.js +34 -51
- package/lib/models/fingerprint.cjs +8 -23
- package/lib/models/fingerprint.js +25 -58
- package/lib/models/form.cjs +106 -142
- package/lib/models/form.js +105 -153
- package/lib/models/form_collection.cjs +63 -101
- package/lib/models/form_collection.js +61 -107
- package/lib/models/page.cjs +106 -129
- package/lib/models/page.js +106 -131
- package/lib/models/push.cjs +278 -322
- package/lib/models/push.js +284 -357
- package/lib/models/query.cjs +21 -41
- package/lib/models/query.js +21 -41
- package/lib/models/session.cjs +33 -65
- package/lib/models/session.js +30 -63
- package/lib/models/user.cjs +29 -51
- package/lib/models/user.js +29 -51
- package/lib/models/utm.cjs +15 -27
- package/lib/models/utm.js +17 -32
- package/lib/models/webchat.cjs +52 -76
- package/lib/models/webchat.js +52 -90
- package/lib/models/whatsapp_widget.cjs +41 -59
- package/lib/models/whatsapp_widget.js +41 -73
- package/lib/vanilla.cjs +4 -7
- package/lib/vanilla.js +2 -1
- package/package.json +1 -9
- package/src/index.bundle.js +0 -1
- package/src/vanilla.js +2 -1
- package/styles/index.css +0 -30
|
@@ -6,189 +6,144 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _application_channel = _interopRequireDefault(require("./application_channel"));
|
|
8
8
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
|
|
16
|
-
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
|
17
|
-
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
|
18
|
-
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
|
19
|
-
function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
|
|
20
|
-
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
|
21
|
-
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
|
22
|
-
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
|
23
|
-
let WebchatChannel = /*#__PURE__*/function (_ApplicationChannel) {
|
|
24
|
-
_inherits(WebchatChannel, _ApplicationChannel);
|
|
25
|
-
var _super = _createSuper(WebchatChannel);
|
|
26
|
-
function WebchatChannel(id, session, conversation) {
|
|
27
|
-
var _this;
|
|
28
|
-
_classCallCheck(this, WebchatChannel);
|
|
29
|
-
_this = _super.call(this);
|
|
30
|
-
_this.id = id;
|
|
31
|
-
_this.session = session;
|
|
32
|
-
_this.conversation = conversation;
|
|
9
|
+
class WebchatChannel extends _application_channel.default {
|
|
10
|
+
constructor(id, session, conversation) {
|
|
11
|
+
super();
|
|
12
|
+
this.id = id;
|
|
13
|
+
this.session = session;
|
|
14
|
+
this.conversation = conversation;
|
|
33
15
|
// Keep our own subscription intent instead of trusting the socket state.
|
|
34
16
|
// The shared WebSocket can reconnect independently, but an explicit
|
|
35
17
|
// unsubscribe means this channel should not silently join again.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
this.subscribed = false;
|
|
19
|
+
this.awaitingReconnectConfirmation = false;
|
|
20
|
+
this.reconnectCallbacks = new Set();
|
|
39
21
|
|
|
40
22
|
// ActionCable confirms subscriptions at the socket level. This channel
|
|
41
23
|
// listens for those confirmations so the controller can wait until Rails has
|
|
42
24
|
// accepted this exact WebchatChannel subscription before fetching missed
|
|
43
25
|
// messages from the REST catch-up endpoint.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return _this;
|
|
26
|
+
this.onSubscriptionConfirmed(identifier => this.handleSubscriptionConfirmed(identifier));
|
|
27
|
+
this.subscribe();
|
|
47
28
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}, {
|
|
79
|
-
key: "resubscribe",
|
|
80
|
-
value: function resubscribe() {
|
|
81
|
-
if (this.subscribed === false) return;
|
|
29
|
+
subscribe() {
|
|
30
|
+
this.subscribed = true;
|
|
31
|
+
const params = {
|
|
32
|
+
channel: 'WebchatChannel',
|
|
33
|
+
id: this.id,
|
|
34
|
+
session: this.session,
|
|
35
|
+
conversation: this.conversation
|
|
36
|
+
};
|
|
37
|
+
this.send({
|
|
38
|
+
command: 'subscribe',
|
|
39
|
+
identifier: params
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
unsubscribe() {
|
|
43
|
+
this.subscribed = false;
|
|
44
|
+
const params = {
|
|
45
|
+
channel: 'WebchatChannel',
|
|
46
|
+
id: this.id,
|
|
47
|
+
session: this.session,
|
|
48
|
+
conversation: this.conversation
|
|
49
|
+
};
|
|
50
|
+
this.send({
|
|
51
|
+
command: 'unsubscribe',
|
|
52
|
+
identifier: params
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
resubscribe() {
|
|
56
|
+
if (this.subscribed === false) return;
|
|
82
57
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}, {
|
|
96
|
-
key: "handleSubscriptionConfirmed",
|
|
97
|
-
value: function handleSubscriptionConfirmed(identifier) {
|
|
98
|
-
if (!this.awaitingReconnectConfirmation || !this.matchesIdentifier(identifier)) return;
|
|
58
|
+
// Reconnect recovery has two phases: send the subscription command first,
|
|
59
|
+
// then wait for ActionCable to confirm it. Catch-up work should run after
|
|
60
|
+
// that confirmation so broadcasts and REST backfill are pointed at the same
|
|
61
|
+
// live conversation subscription.
|
|
62
|
+
this.awaitingReconnectConfirmation = true;
|
|
63
|
+
this.subscribe();
|
|
64
|
+
}
|
|
65
|
+
onReconnect(callback) {
|
|
66
|
+
this.reconnectCallbacks.add(callback);
|
|
67
|
+
}
|
|
68
|
+
handleSubscriptionConfirmed(identifier) {
|
|
69
|
+
if (!this.awaitingReconnectConfirmation || !this.matchesIdentifier(identifier)) return;
|
|
99
70
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
71
|
+
// Only the first matching confirmation completes the reconnect cycle. This
|
|
72
|
+
// prevents unrelated subscription confirmations on the shared socket from
|
|
73
|
+
// triggering duplicate catch-up requests.
|
|
74
|
+
this.awaitingReconnectConfirmation = false;
|
|
75
|
+
this.reconnectCallbacks.forEach(callback => callback());
|
|
76
|
+
}
|
|
77
|
+
matchesIdentifier(identifier) {
|
|
78
|
+
let params;
|
|
79
|
+
try {
|
|
80
|
+
params = typeof identifier === 'string' ? JSON.parse(identifier) : identifier;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
105
83
|
}
|
|
106
|
-
}, {
|
|
107
|
-
key: "matchesIdentifier",
|
|
108
|
-
value: function matchesIdentifier(identifier) {
|
|
109
|
-
let params;
|
|
110
|
-
try {
|
|
111
|
-
params = typeof identifier === 'string' ? JSON.parse(identifier) : identifier;
|
|
112
|
-
} catch {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
84
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
85
|
+
// ActionCable sends the same identifier payload we used when subscribing.
|
|
86
|
+
// Matching every routing key keeps a confirmation for another webchat,
|
|
87
|
+
// session, or conversation from being treated as this channel's reconnect.
|
|
88
|
+
return params.channel === 'WebchatChannel' && params.id === this.id && params.session === this.session && params.conversation === this.conversation;
|
|
89
|
+
}
|
|
90
|
+
startTypingIndicator() {
|
|
91
|
+
const params = {
|
|
92
|
+
channel: 'WebchatChannel',
|
|
93
|
+
id: this.id,
|
|
94
|
+
session: this.session,
|
|
95
|
+
conversation: this.conversation
|
|
96
|
+
};
|
|
97
|
+
this.send({
|
|
98
|
+
command: 'message',
|
|
99
|
+
identifier: params,
|
|
100
|
+
data: {
|
|
101
|
+
action: 'started_typing'
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
stopTypingIndicator() {
|
|
106
|
+
const params = {
|
|
107
|
+
channel: 'WebchatChannel',
|
|
108
|
+
id: this.id,
|
|
109
|
+
session: this.session,
|
|
110
|
+
conversation: this.conversation
|
|
111
|
+
};
|
|
112
|
+
this.send({
|
|
113
|
+
command: 'typing:stop',
|
|
114
|
+
identifier: params,
|
|
115
|
+
data: {
|
|
116
|
+
action: 'stopped_typing'
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
onMessage(callback) {
|
|
121
|
+
super.onMessage(message => {
|
|
122
|
+
if (message.type !== 'message') return;
|
|
123
|
+
callback(message);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
onReaction(callback) {
|
|
127
|
+
super.onMessage(message => {
|
|
128
|
+
if (message.type === 'reaction.create' || message.type === 'reaction.destroy') {
|
|
160
129
|
callback(message);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}, {
|
|
182
|
-
key: "updateSubscriptionWith",
|
|
183
|
-
value: function updateSubscriptionWith(conversation) {
|
|
184
|
-
this.unsubscribe();
|
|
185
|
-
setTimeout(() => {
|
|
186
|
-
this.conversation = conversation;
|
|
187
|
-
this.subscribe();
|
|
188
|
-
}, 1000);
|
|
189
|
-
}
|
|
190
|
-
}]);
|
|
191
|
-
return WebchatChannel;
|
|
192
|
-
}(_application_channel.default);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
onTypingStart(callback) {
|
|
134
|
+
super.onMessage(message => {
|
|
135
|
+
if (message.type === 'started_typing') {
|
|
136
|
+
callback(message);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
updateSubscriptionWith(conversation) {
|
|
141
|
+
this.unsubscribe();
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
this.conversation = conversation;
|
|
144
|
+
this.subscribe();
|
|
145
|
+
}, 1000);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
193
148
|
var _default = WebchatChannel;
|
|
194
149
|
exports.default = _default;
|
|
@@ -1,186 +1,141 @@
|
|
|
1
|
-
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
2
|
-
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); } }
|
|
3
|
-
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
4
|
-
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
5
|
-
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); }
|
|
6
|
-
function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); }
|
|
7
|
-
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
|
|
8
|
-
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
|
9
|
-
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
|
10
|
-
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
|
11
|
-
function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
|
|
12
|
-
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
|
13
|
-
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
|
14
|
-
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
|
15
1
|
import ApplicationChannel from './application_channel';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
_this = _super.call(this);
|
|
23
|
-
_this.id = id;
|
|
24
|
-
_this.session = session;
|
|
25
|
-
_this.conversation = conversation;
|
|
2
|
+
class WebchatChannel extends ApplicationChannel {
|
|
3
|
+
constructor(id, session, conversation) {
|
|
4
|
+
super();
|
|
5
|
+
this.id = id;
|
|
6
|
+
this.session = session;
|
|
7
|
+
this.conversation = conversation;
|
|
26
8
|
// Keep our own subscription intent instead of trusting the socket state.
|
|
27
9
|
// The shared WebSocket can reconnect independently, but an explicit
|
|
28
10
|
// unsubscribe means this channel should not silently join again.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
11
|
+
this.subscribed = false;
|
|
12
|
+
this.awaitingReconnectConfirmation = false;
|
|
13
|
+
this.reconnectCallbacks = new Set();
|
|
32
14
|
|
|
33
15
|
// ActionCable confirms subscriptions at the socket level. This channel
|
|
34
16
|
// listens for those confirmations so the controller can wait until Rails has
|
|
35
17
|
// accepted this exact WebchatChannel subscription before fetching missed
|
|
36
18
|
// messages from the REST catch-up endpoint.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return _this;
|
|
19
|
+
this.onSubscriptionConfirmed(identifier => this.handleSubscriptionConfirmed(identifier));
|
|
20
|
+
this.subscribe();
|
|
40
21
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}, {
|
|
72
|
-
key: "resubscribe",
|
|
73
|
-
value: function resubscribe() {
|
|
74
|
-
if (this.subscribed === false) return;
|
|
22
|
+
subscribe() {
|
|
23
|
+
this.subscribed = true;
|
|
24
|
+
const params = {
|
|
25
|
+
channel: 'WebchatChannel',
|
|
26
|
+
id: this.id,
|
|
27
|
+
session: this.session,
|
|
28
|
+
conversation: this.conversation
|
|
29
|
+
};
|
|
30
|
+
this.send({
|
|
31
|
+
command: 'subscribe',
|
|
32
|
+
identifier: params
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
unsubscribe() {
|
|
36
|
+
this.subscribed = false;
|
|
37
|
+
const params = {
|
|
38
|
+
channel: 'WebchatChannel',
|
|
39
|
+
id: this.id,
|
|
40
|
+
session: this.session,
|
|
41
|
+
conversation: this.conversation
|
|
42
|
+
};
|
|
43
|
+
this.send({
|
|
44
|
+
command: 'unsubscribe',
|
|
45
|
+
identifier: params
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
resubscribe() {
|
|
49
|
+
if (this.subscribed === false) return;
|
|
75
50
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}, {
|
|
89
|
-
key: "handleSubscriptionConfirmed",
|
|
90
|
-
value: function handleSubscriptionConfirmed(identifier) {
|
|
91
|
-
if (!this.awaitingReconnectConfirmation || !this.matchesIdentifier(identifier)) return;
|
|
51
|
+
// Reconnect recovery has two phases: send the subscription command first,
|
|
52
|
+
// then wait for ActionCable to confirm it. Catch-up work should run after
|
|
53
|
+
// that confirmation so broadcasts and REST backfill are pointed at the same
|
|
54
|
+
// live conversation subscription.
|
|
55
|
+
this.awaitingReconnectConfirmation = true;
|
|
56
|
+
this.subscribe();
|
|
57
|
+
}
|
|
58
|
+
onReconnect(callback) {
|
|
59
|
+
this.reconnectCallbacks.add(callback);
|
|
60
|
+
}
|
|
61
|
+
handleSubscriptionConfirmed(identifier) {
|
|
62
|
+
if (!this.awaitingReconnectConfirmation || !this.matchesIdentifier(identifier)) return;
|
|
92
63
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
64
|
+
// Only the first matching confirmation completes the reconnect cycle. This
|
|
65
|
+
// prevents unrelated subscription confirmations on the shared socket from
|
|
66
|
+
// triggering duplicate catch-up requests.
|
|
67
|
+
this.awaitingReconnectConfirmation = false;
|
|
68
|
+
this.reconnectCallbacks.forEach(callback => callback());
|
|
69
|
+
}
|
|
70
|
+
matchesIdentifier(identifier) {
|
|
71
|
+
let params;
|
|
72
|
+
try {
|
|
73
|
+
params = typeof identifier === 'string' ? JSON.parse(identifier) : identifier;
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
98
76
|
}
|
|
99
|
-
}, {
|
|
100
|
-
key: "matchesIdentifier",
|
|
101
|
-
value: function matchesIdentifier(identifier) {
|
|
102
|
-
var params;
|
|
103
|
-
try {
|
|
104
|
-
params = typeof identifier === 'string' ? JSON.parse(identifier) : identifier;
|
|
105
|
-
} catch (_unused) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
77
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
78
|
+
// ActionCable sends the same identifier payload we used when subscribing.
|
|
79
|
+
// Matching every routing key keeps a confirmation for another webchat,
|
|
80
|
+
// session, or conversation from being treated as this channel's reconnect.
|
|
81
|
+
return params.channel === 'WebchatChannel' && params.id === this.id && params.session === this.session && params.conversation === this.conversation;
|
|
82
|
+
}
|
|
83
|
+
startTypingIndicator() {
|
|
84
|
+
const params = {
|
|
85
|
+
channel: 'WebchatChannel',
|
|
86
|
+
id: this.id,
|
|
87
|
+
session: this.session,
|
|
88
|
+
conversation: this.conversation
|
|
89
|
+
};
|
|
90
|
+
this.send({
|
|
91
|
+
command: 'message',
|
|
92
|
+
identifier: params,
|
|
93
|
+
data: {
|
|
94
|
+
action: 'started_typing'
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
stopTypingIndicator() {
|
|
99
|
+
const params = {
|
|
100
|
+
channel: 'WebchatChannel',
|
|
101
|
+
id: this.id,
|
|
102
|
+
session: this.session,
|
|
103
|
+
conversation: this.conversation
|
|
104
|
+
};
|
|
105
|
+
this.send({
|
|
106
|
+
command: 'typing:stop',
|
|
107
|
+
identifier: params,
|
|
108
|
+
data: {
|
|
109
|
+
action: 'stopped_typing'
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
onMessage(callback) {
|
|
114
|
+
super.onMessage(message => {
|
|
115
|
+
if (message.type !== 'message') return;
|
|
116
|
+
callback(message);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
onReaction(callback) {
|
|
120
|
+
super.onMessage(message => {
|
|
121
|
+
if (message.type === 'reaction.create' || message.type === 'reaction.destroy') {
|
|
153
122
|
callback(message);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
}, {
|
|
175
|
-
key: "updateSubscriptionWith",
|
|
176
|
-
value: function updateSubscriptionWith(conversation) {
|
|
177
|
-
this.unsubscribe();
|
|
178
|
-
setTimeout(() => {
|
|
179
|
-
this.conversation = conversation;
|
|
180
|
-
this.subscribe();
|
|
181
|
-
}, 1000);
|
|
182
|
-
}
|
|
183
|
-
}]);
|
|
184
|
-
return WebchatChannel;
|
|
185
|
-
}(ApplicationChannel);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
onTypingStart(callback) {
|
|
127
|
+
super.onMessage(message => {
|
|
128
|
+
if (message.type === 'started_typing') {
|
|
129
|
+
callback(message);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
updateSubscriptionWith(conversation) {
|
|
134
|
+
this.unsubscribe();
|
|
135
|
+
setTimeout(() => {
|
|
136
|
+
this.conversation = conversation;
|
|
137
|
+
this.subscribe();
|
|
138
|
+
}, 1000);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
186
141
|
export default WebchatChannel;
|