@hellotext/hellotext 2.5.6 → 2.5.7
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 +5 -1
- package/dist/hellotext.js +1 -1
- package/dist/hellotext.js.LICENSE.txt +1 -1
- package/index.d.ts +15 -0
- package/lib/api/index.cjs +6 -0
- package/lib/api/index.js +6 -0
- package/lib/api/push/identities.cjs +59 -0
- package/lib/api/push/identities.js +68 -0
- package/lib/core/configuration/push.cjs +28 -0
- package/lib/core/configuration/push.js +22 -0
- package/lib/core/configuration.cjs +6 -1
- package/lib/core/configuration.js +5 -0
- package/lib/hellotext.cjs +14 -1
- package/lib/hellotext.js +14 -2
- package/lib/models/business.cjs +1 -0
- package/lib/models/business.js +1 -0
- package/lib/models/index.cjs +7 -0
- package/lib/models/index.js +1 -0
- package/lib/models/push.cjs +389 -0
- package/lib/models/push.js +412 -0
- package/package.json +2 -2
- package/src/api/index.js +5 -0
- package/src/api/push/identities.js +42 -0
- package/src/core/configuration/push.js +12 -0
- package/src/core/configuration.js +5 -0
- package/src/hellotext.js +15 -1
- package/src/models/business.js +1 -0
- package/src/models/index.js +1 -0
- package/src/models/push.js +397 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
3
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
5
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
6
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
7
|
+
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); } }
|
|
8
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
9
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
10
|
+
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); }
|
|
11
|
+
import API from '../api';
|
|
12
|
+
import { Configuration } from '../core';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Manages browser Push subscriptions for Hellotext.
|
|
16
|
+
*
|
|
17
|
+
* @property {Promise<void>|null} ready - Initialization promise, available after initialize().
|
|
18
|
+
*/
|
|
19
|
+
var Push = /*#__PURE__*/function () {
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object} data - Push configuration from the business response.
|
|
22
|
+
* @param {String} data.public_key - Base64url-encoded VAPID public key.
|
|
23
|
+
*/
|
|
24
|
+
function Push(data) {
|
|
25
|
+
_classCallCheck(this, Push);
|
|
26
|
+
this.publicKey = data.public_key;
|
|
27
|
+
this.serviceWorkerUrl = Configuration.push.serviceWorkerUrl;
|
|
28
|
+
this.channelId = Configuration.push.channelId;
|
|
29
|
+
this.ready = null;
|
|
30
|
+
this.registrationPromise = null;
|
|
31
|
+
this.subscribePromise = null;
|
|
32
|
+
this.unsubscribePromise = null;
|
|
33
|
+
this.syncPromise = null;
|
|
34
|
+
this.subscription = null;
|
|
35
|
+
this.retryTimeout = null;
|
|
36
|
+
this.retryAttempts = 0;
|
|
37
|
+
this.disposed = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Prepares the service worker and restores an existing subscription.
|
|
42
|
+
*
|
|
43
|
+
* @returns {Promise<void>}
|
|
44
|
+
*/
|
|
45
|
+
_createClass(Push, [{
|
|
46
|
+
key: "initialize",
|
|
47
|
+
value: function initialize() {
|
|
48
|
+
this.ready = this.restoreSubscription();
|
|
49
|
+
return this.ready;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Registers an existing Hellotext subscription with the server without prompting.
|
|
54
|
+
*
|
|
55
|
+
* @private
|
|
56
|
+
* @returns {Promise<void>}
|
|
57
|
+
*/
|
|
58
|
+
}, {
|
|
59
|
+
key: "restoreSubscription",
|
|
60
|
+
value: function () {
|
|
61
|
+
var _restoreSubscription = _asyncToGenerator(function* () {
|
|
62
|
+
var registration = yield this.getRegistration();
|
|
63
|
+
var subscription = yield registration.pushManager.getSubscription();
|
|
64
|
+
if (this.disposed || this.unsubscribePromise || !subscription || !this.owns(subscription)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
this.subscription = subscription;
|
|
68
|
+
yield this.sync();
|
|
69
|
+
});
|
|
70
|
+
function restoreSubscription() {
|
|
71
|
+
return _restoreSubscription.apply(this, arguments);
|
|
72
|
+
}
|
|
73
|
+
return restoreSubscription;
|
|
74
|
+
}()
|
|
75
|
+
/**
|
|
76
|
+
* Creates or reuses a Push subscription. Call from a user click handler.
|
|
77
|
+
* Concurrent calls share the same pending request.
|
|
78
|
+
*
|
|
79
|
+
* @returns {Promise<import('../api/response').Response|void>} Server registration response,
|
|
80
|
+
* or no value when disposed.
|
|
81
|
+
* Rejects when permission is denied or a browser or network operation fails.
|
|
82
|
+
*/
|
|
83
|
+
}, {
|
|
84
|
+
key: "subscribe",
|
|
85
|
+
value: function subscribe() {
|
|
86
|
+
if (this.disposed) return Promise.resolve();
|
|
87
|
+
if (this.unsubscribePromise) return Promise.reject(new Error('Push unsubscribe is in progress'));
|
|
88
|
+
if (this.subscribePromise) return this.subscribePromise;
|
|
89
|
+
|
|
90
|
+
// Request permission before awaiting worker readiness to retain the click's user activation.
|
|
91
|
+
var permission = Notification.permission === 'default' ? Notification.requestPermission() : Promise.resolve(Notification.permission);
|
|
92
|
+
this.subscribePromise = this.createSubscription(permission).finally(() => {
|
|
93
|
+
this.subscribePromise = null;
|
|
94
|
+
});
|
|
95
|
+
return this.subscribePromise;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Waits for permission and worker readiness, then subscribes and registers with Hellotext.
|
|
100
|
+
*
|
|
101
|
+
* @private
|
|
102
|
+
* @param {Promise<NotificationPermission>} permission - Pending notification permission result.
|
|
103
|
+
* @returns {Promise<import('../api/response').Response|void>}
|
|
104
|
+
*/
|
|
105
|
+
}, {
|
|
106
|
+
key: "createSubscription",
|
|
107
|
+
value: function () {
|
|
108
|
+
var _createSubscription = _asyncToGenerator(function* (permission) {
|
|
109
|
+
if ((yield permission) !== 'granted') throw new Error('Push permission was not granted');
|
|
110
|
+
var registration = yield this.getRegistration();
|
|
111
|
+
if (this.disposed) return;
|
|
112
|
+
var subscription = yield registration.pushManager.getSubscription();
|
|
113
|
+
if (this.disposed) return;
|
|
114
|
+
if (subscription && !this.owns(subscription)) {
|
|
115
|
+
throw new Error('The existing Push subscription belongs to a different application');
|
|
116
|
+
}
|
|
117
|
+
subscription || (subscription = yield registration.pushManager.subscribe({
|
|
118
|
+
userVisibleOnly: true,
|
|
119
|
+
applicationServerKey: this.applicationServerKey
|
|
120
|
+
}));
|
|
121
|
+
if (this.disposed) return;
|
|
122
|
+
this.subscription = subscription;
|
|
123
|
+
return this.sync();
|
|
124
|
+
});
|
|
125
|
+
function createSubscription(_x) {
|
|
126
|
+
return _createSubscription.apply(this, arguments);
|
|
127
|
+
}
|
|
128
|
+
return createSubscription;
|
|
129
|
+
}()
|
|
130
|
+
/**
|
|
131
|
+
* Disables the server identity and removes the browser subscription.
|
|
132
|
+
* Keeps the subscription when the server request fails so the caller can retry.
|
|
133
|
+
*
|
|
134
|
+
* @returns {Promise<import('../api/response').Response|null|void>} Server response, null when
|
|
135
|
+
* no subscription exists, or no value when disposed. Rejects when a browser or network
|
|
136
|
+
* operation fails.
|
|
137
|
+
*/
|
|
138
|
+
}, {
|
|
139
|
+
key: "unsubscribe",
|
|
140
|
+
value: function unsubscribe() {
|
|
141
|
+
if (this.disposed) return Promise.resolve();
|
|
142
|
+
if (this.unsubscribePromise) return this.unsubscribePromise;
|
|
143
|
+
this.clearRetryTimeout();
|
|
144
|
+
this.unsubscribePromise = this.removeSubscription().finally(() => {
|
|
145
|
+
this.unsubscribePromise = null;
|
|
146
|
+
});
|
|
147
|
+
return this.unsubscribePromise;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Finishes pending registration before disabling and removing the subscription.
|
|
152
|
+
*
|
|
153
|
+
* @private
|
|
154
|
+
* @returns {Promise<import('../api/response').Response|null|void>}
|
|
155
|
+
*/
|
|
156
|
+
}, {
|
|
157
|
+
key: "removeSubscription",
|
|
158
|
+
value: function () {
|
|
159
|
+
var _removeSubscription = _asyncToGenerator(function* () {
|
|
160
|
+
var _this$ready, _this$subscribePromis, _this$syncPromise;
|
|
161
|
+
yield (_this$ready = this.ready) === null || _this$ready === void 0 ? void 0 : _this$ready.catch(() => {});
|
|
162
|
+
yield (_this$subscribePromis = this.subscribePromise) === null || _this$subscribePromis === void 0 ? void 0 : _this$subscribePromis.catch(() => {});
|
|
163
|
+
yield (_this$syncPromise = this.syncPromise) === null || _this$syncPromise === void 0 ? void 0 : _this$syncPromise.catch(() => {});
|
|
164
|
+
var registration = yield this.getRegistration();
|
|
165
|
+
var subscription = (yield registration.pushManager.getSubscription()) || this.subscription;
|
|
166
|
+
if (this.disposed) return;
|
|
167
|
+
if (!subscription) return null;
|
|
168
|
+
if (!this.owns(subscription)) {
|
|
169
|
+
throw new Error('The existing Push subscription belongs to a different application');
|
|
170
|
+
}
|
|
171
|
+
this.subscription = subscription;
|
|
172
|
+
var response = yield API.pushIdentities.destroy({
|
|
173
|
+
subscription: subscription.toJSON()
|
|
174
|
+
});
|
|
175
|
+
if (response.failed) return response;
|
|
176
|
+
if (this.disposed) return;
|
|
177
|
+
yield subscription.unsubscribe();
|
|
178
|
+
this.subscription = null;
|
|
179
|
+
this.clearRetryTimeout();
|
|
180
|
+
return response;
|
|
181
|
+
});
|
|
182
|
+
function removeSubscription() {
|
|
183
|
+
return _removeSubscription.apply(this, arguments);
|
|
184
|
+
}
|
|
185
|
+
return removeSubscription;
|
|
186
|
+
}()
|
|
187
|
+
/**
|
|
188
|
+
* Registers the current subscription, sharing any request already in progress.
|
|
189
|
+
*
|
|
190
|
+
* @private
|
|
191
|
+
* @returns {Promise<import('../api/response').Response|void>}
|
|
192
|
+
*/
|
|
193
|
+
}, {
|
|
194
|
+
key: "sync",
|
|
195
|
+
value: function sync() {
|
|
196
|
+
if (this.disposed) return Promise.resolve();
|
|
197
|
+
if (this.syncPromise) return this.syncPromise;
|
|
198
|
+
this.clearRetryTimeout();
|
|
199
|
+
this.syncPromise = this.registerIdentity().finally(() => {
|
|
200
|
+
this.syncPromise = null;
|
|
201
|
+
});
|
|
202
|
+
return this.syncPromise;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Sends the subscription to Hellotext and schedules a retry on failure.
|
|
207
|
+
*
|
|
208
|
+
* @private
|
|
209
|
+
* @returns {Promise<import('../api/response').Response>}
|
|
210
|
+
*/
|
|
211
|
+
}, {
|
|
212
|
+
key: "registerIdentity",
|
|
213
|
+
value: function () {
|
|
214
|
+
var _registerIdentity = _asyncToGenerator(function* () {
|
|
215
|
+
try {
|
|
216
|
+
var response = yield API.pushIdentities.create(_objectSpread({
|
|
217
|
+
subscription: this.subscription.toJSON()
|
|
218
|
+
}, this.channelId ? {
|
|
219
|
+
channel_id: this.channelId
|
|
220
|
+
} : {}));
|
|
221
|
+
if (response.succeeded) {
|
|
222
|
+
this.retryAttempts = 0;
|
|
223
|
+
} else {
|
|
224
|
+
this.scheduleRetry();
|
|
225
|
+
}
|
|
226
|
+
return response;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
this.scheduleRetry();
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
function registerIdentity() {
|
|
233
|
+
return _registerIdentity.apply(this, arguments);
|
|
234
|
+
}
|
|
235
|
+
return registerIdentity;
|
|
236
|
+
}()
|
|
237
|
+
/**
|
|
238
|
+
* Schedules up to three registration retries with increasing delays.
|
|
239
|
+
*
|
|
240
|
+
* @private
|
|
241
|
+
* @returns {void}
|
|
242
|
+
*/
|
|
243
|
+
}, {
|
|
244
|
+
key: "scheduleRetry",
|
|
245
|
+
value: function scheduleRetry() {
|
|
246
|
+
if (this.disposed || this.unsubscribePromise || this.retryTimeout || this.retryAttempts >= 3) return;
|
|
247
|
+
this.retryTimeout = setTimeout(() => {
|
|
248
|
+
this.retryTimeout = null;
|
|
249
|
+
this.sync().catch(() => {});
|
|
250
|
+
}, 1000 * 2 ** this.retryAttempts);
|
|
251
|
+
this.retryAttempts += 1;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Cancels a pending registration retry.
|
|
256
|
+
*
|
|
257
|
+
* @private
|
|
258
|
+
* @returns {void}
|
|
259
|
+
*/
|
|
260
|
+
}, {
|
|
261
|
+
key: "clearRetryTimeout",
|
|
262
|
+
value: function clearRetryTimeout() {
|
|
263
|
+
clearTimeout(this.retryTimeout);
|
|
264
|
+
this.retryTimeout = null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Stops registration retries and prevents further use of this instance.
|
|
269
|
+
*
|
|
270
|
+
* @returns {void}
|
|
271
|
+
*/
|
|
272
|
+
}, {
|
|
273
|
+
key: "dispose",
|
|
274
|
+
value: function dispose() {
|
|
275
|
+
this.disposed = true;
|
|
276
|
+
this.clearRetryTimeout();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Whether this instance has a browser subscription, regardless of server registration.
|
|
281
|
+
*
|
|
282
|
+
* @returns {Boolean}
|
|
283
|
+
*/
|
|
284
|
+
}, {
|
|
285
|
+
key: "subscribed",
|
|
286
|
+
get: function get() {
|
|
287
|
+
return !!this.subscription;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Decodes the public key into the bytes expected by PushManager.subscribe().
|
|
292
|
+
*
|
|
293
|
+
* @private
|
|
294
|
+
* @returns {Uint8Array}
|
|
295
|
+
*/
|
|
296
|
+
}, {
|
|
297
|
+
key: "applicationServerKey",
|
|
298
|
+
get: function get() {
|
|
299
|
+
var base64 = this.publicKey.replace(/-/g, '+').replace(/_/g, '/');
|
|
300
|
+
return Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, '=')), char => char.charCodeAt(0));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Checks whether a subscription uses this instance's public key.
|
|
305
|
+
*
|
|
306
|
+
* @private
|
|
307
|
+
* @param {PushSubscription} subscription - Browser subscription to check.
|
|
308
|
+
* @returns {Boolean}
|
|
309
|
+
*/
|
|
310
|
+
}, {
|
|
311
|
+
key: "owns",
|
|
312
|
+
value: function owns(subscription) {
|
|
313
|
+
var _subscription$options;
|
|
314
|
+
var key = (_subscription$options = subscription.options) === null || _subscription$options === void 0 ? void 0 : _subscription$options.applicationServerKey;
|
|
315
|
+
if (!key) return false;
|
|
316
|
+
var actual = new Uint8Array(key);
|
|
317
|
+
var expected = this.applicationServerKey;
|
|
318
|
+
return actual.length === expected.length && actual.every((value, index) => value === expected[index]);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Gets the worker registration, sharing any pending lookup.
|
|
323
|
+
* Failed lookups can be retried by a later call.
|
|
324
|
+
*
|
|
325
|
+
* @private
|
|
326
|
+
* @returns {Promise<ServiceWorkerRegistration>}
|
|
327
|
+
*/
|
|
328
|
+
}, {
|
|
329
|
+
key: "getRegistration",
|
|
330
|
+
value: function getRegistration() {
|
|
331
|
+
if (!this.registrationPromise) {
|
|
332
|
+
this.registrationPromise = this.loadRegistration().catch(error => {
|
|
333
|
+
this.registrationPromise = null;
|
|
334
|
+
throw error;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return this.registrationPromise;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Registers the configured worker or waits for the page's existing registration.
|
|
342
|
+
*
|
|
343
|
+
* @private
|
|
344
|
+
* @returns {Promise<ServiceWorkerRegistration>} An active registration.
|
|
345
|
+
* Rejects if registration fails or the worker readiness wait times out.
|
|
346
|
+
*/
|
|
347
|
+
}, {
|
|
348
|
+
key: "loadRegistration",
|
|
349
|
+
value: function () {
|
|
350
|
+
var _loadRegistration = _asyncToGenerator(function* () {
|
|
351
|
+
if (this.serviceWorkerUrl) {
|
|
352
|
+
var registration = yield navigator.serviceWorker.register(this.serviceWorkerUrl);
|
|
353
|
+
if (registration.active && !registration.installing && !registration.waiting) {
|
|
354
|
+
// A returning visitor may have an older worker that does not include our Push handlers.
|
|
355
|
+
// Calling register() with the same URL can return that existing registration without
|
|
356
|
+
// checking whether the script served at that URL has changed since their last visit.
|
|
357
|
+
//
|
|
358
|
+
// If no replacement is already installing or waiting, explicitly check for an update
|
|
359
|
+
// before treating the active worker as ready. Awaiting update() completes the update
|
|
360
|
+
// check, but does not wait for a replacement worker to activate. The code below selects
|
|
361
|
+
// that replacement, if one was found, and waits for its activation before subscribing.
|
|
362
|
+
yield registration.update();
|
|
363
|
+
}
|
|
364
|
+
var worker = registration.installing || registration.waiting || registration.active;
|
|
365
|
+
if ((worker === null || worker === void 0 ? void 0 : worker.state) === 'activated') return registration;
|
|
366
|
+
return new Promise((resolve, reject) => {
|
|
367
|
+
var timeout = setTimeout(() => finish(new Error('Push service worker did not become active')), 10000);
|
|
368
|
+
var finish = error => {
|
|
369
|
+
clearTimeout(timeout);
|
|
370
|
+
worker === null || worker === void 0 ? void 0 : worker.removeEventListener('statechange', changed);
|
|
371
|
+
error ? reject(error) : resolve(registration);
|
|
372
|
+
};
|
|
373
|
+
var changed = () => {
|
|
374
|
+
if ((worker === null || worker === void 0 ? void 0 : worker.state) === 'activated') finish();
|
|
375
|
+
if ((worker === null || worker === void 0 ? void 0 : worker.state) === 'redundant') finish(new Error('Push service worker installation failed'));
|
|
376
|
+
};
|
|
377
|
+
worker === null || worker === void 0 ? void 0 : worker.addEventListener('statechange', changed);
|
|
378
|
+
changed();
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Reuse the service worker already registered for this page.
|
|
383
|
+
return new Promise((resolve, reject) => {
|
|
384
|
+
var timeout = setTimeout(() => reject(new Error('Push service worker is not available')), 10000);
|
|
385
|
+
navigator.serviceWorker.ready.then(registration => {
|
|
386
|
+
clearTimeout(timeout);
|
|
387
|
+
resolve(registration);
|
|
388
|
+
}, error => {
|
|
389
|
+
clearTimeout(timeout);
|
|
390
|
+
reject(error);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
function loadRegistration() {
|
|
395
|
+
return _loadRegistration.apply(this, arguments);
|
|
396
|
+
}
|
|
397
|
+
return loadRegistration;
|
|
398
|
+
}()
|
|
399
|
+
/**
|
|
400
|
+
* Whether the current page provides the browser APIs required for Push.
|
|
401
|
+
*
|
|
402
|
+
* @returns {Boolean}
|
|
403
|
+
*/
|
|
404
|
+
}], [{
|
|
405
|
+
key: "supported",
|
|
406
|
+
get: function get() {
|
|
407
|
+
return typeof window !== 'undefined' && window.isSecureContext === true && typeof navigator !== 'undefined' && 'serviceWorker' in navigator && typeof PushManager !== 'undefined' && typeof Notification !== 'undefined';
|
|
408
|
+
}
|
|
409
|
+
}]);
|
|
410
|
+
return Push;
|
|
411
|
+
}();
|
|
412
|
+
export { Push };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hellotext/hellotext",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.7",
|
|
4
4
|
"description": "Hellotext JavaScript Client",
|
|
5
5
|
"source": "src/index.js",
|
|
6
6
|
"main": "lib/index.cjs",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@emoji-mart/data": "^1.2.1",
|
|
95
95
|
"@floating-ui/dom": "^1.7.3",
|
|
96
96
|
"@hotwired/stimulus": "^3.0.0",
|
|
97
|
-
"dompurify": "^3.4.
|
|
97
|
+
"dompurify": "^3.4.14",
|
|
98
98
|
"emoji-mart": "^5.6.0"
|
|
99
99
|
},
|
|
100
100
|
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610",
|
package/src/api/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import IdentificationsAPI from './identifications'
|
|
|
5
5
|
import WebchatsAPI from './webchats'
|
|
6
6
|
import WhatsAppWidgetsAPI from './whatsapp_widgets'
|
|
7
7
|
import AcksAPI from './acks'
|
|
8
|
+
import PushIdentitiesAPI from './push/identities'
|
|
8
9
|
|
|
9
10
|
// Browsers keep `fetch(..., { keepalive: true })` requests alive during page
|
|
10
11
|
// unload/navigation, which is exactly the failure mode for analytics events
|
|
@@ -57,6 +58,10 @@ export default class API {
|
|
|
57
58
|
static get acks() {
|
|
58
59
|
return AcksAPI
|
|
59
60
|
}
|
|
61
|
+
|
|
62
|
+
static get pushIdentities() {
|
|
63
|
+
return PushIdentitiesAPI
|
|
64
|
+
}
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
export { Response } from './response'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Hellotext from '../../hellotext'
|
|
2
|
+
|
|
3
|
+
import { Configuration } from '../../core'
|
|
4
|
+
import { Response } from '../response'
|
|
5
|
+
|
|
6
|
+
class PushIdentitiesAPI {
|
|
7
|
+
static get endpoint() {
|
|
8
|
+
return Configuration.endpoint('public/push/identities')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static async create(data = {}) {
|
|
12
|
+
const response = await fetch(this.endpoint, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
keepalive: true,
|
|
15
|
+
headers: Hellotext.headers,
|
|
16
|
+
body: JSON.stringify({
|
|
17
|
+
...data,
|
|
18
|
+
session: Hellotext.session,
|
|
19
|
+
origin: window.location.origin,
|
|
20
|
+
}),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return new Response(response.ok, response)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static async destroy(data = {}) {
|
|
27
|
+
const response = await fetch(this.endpoint, {
|
|
28
|
+
method: 'DELETE',
|
|
29
|
+
keepalive: true,
|
|
30
|
+
headers: Hellotext.headers,
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
...data,
|
|
33
|
+
session: Hellotext.session,
|
|
34
|
+
origin: window.location.origin,
|
|
35
|
+
}),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return new Response(response.ok, response)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default PushIdentitiesAPI
|
|
@@ -2,6 +2,7 @@ import { Forms } from './configuration/forms'
|
|
|
2
2
|
import { Locale } from './configuration/locale'
|
|
3
3
|
import { Webchat } from './configuration/webchat'
|
|
4
4
|
import { WhatsApp } from './configuration/whatsapp'
|
|
5
|
+
import { Push } from './configuration/push'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @class Configuration
|
|
@@ -12,6 +13,7 @@ import { WhatsApp } from './configuration/whatsapp'
|
|
|
12
13
|
* @property {Forms} [forms] - form configuration
|
|
13
14
|
* @property {Webchat} [webchat] - webchat configuration
|
|
14
15
|
* @property {WhatsApp} [whatsappWidget] - WhatsApp widget configuration
|
|
16
|
+
* @property {Push} [push] - push subscription configuration
|
|
15
17
|
* @property {Locale} [locale] - locale configuration
|
|
16
18
|
*/
|
|
17
19
|
class Configuration {
|
|
@@ -24,6 +26,7 @@ class Configuration {
|
|
|
24
26
|
static forms = Forms
|
|
25
27
|
static webchat = Webchat
|
|
26
28
|
static whatsapp = WhatsApp
|
|
29
|
+
static push = Push
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
32
|
*
|
|
@@ -47,6 +50,8 @@ class Configuration {
|
|
|
47
50
|
this.webchat = Webchat.assign(value)
|
|
48
51
|
} else if (key === 'whatsappWidget') {
|
|
49
52
|
this.whatsapp = WhatsApp.assign(value)
|
|
53
|
+
} else if (key === 'push') {
|
|
54
|
+
this.push = Push.assign(value)
|
|
50
55
|
} else {
|
|
51
56
|
this[key] = value
|
|
52
57
|
}
|
package/src/hellotext.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Fingerprint,
|
|
7
7
|
FormCollection,
|
|
8
8
|
Page,
|
|
9
|
+
Push,
|
|
9
10
|
Query,
|
|
10
11
|
Session,
|
|
11
12
|
User,
|
|
@@ -21,6 +22,7 @@ class Hellotext {
|
|
|
21
22
|
static business
|
|
22
23
|
static webchat
|
|
23
24
|
static whatsapp
|
|
25
|
+
static push
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* initialize the module.
|
|
@@ -28,10 +30,13 @@ class Hellotext {
|
|
|
28
30
|
* @param { Configuration } config
|
|
29
31
|
*/
|
|
30
32
|
static async initialize(business, config = {}) {
|
|
33
|
+
this.push?.dispose()
|
|
34
|
+
this.push = null
|
|
35
|
+
|
|
31
36
|
this.business = new Business(business)
|
|
32
37
|
this.page = new Page()
|
|
33
38
|
|
|
34
|
-
Configuration.assign(config)
|
|
39
|
+
Configuration.assign({ push: {}, ...config })
|
|
35
40
|
Session.initialize(this.page)
|
|
36
41
|
|
|
37
42
|
this.forms = new FormCollection()
|
|
@@ -39,6 +44,15 @@ class Hellotext {
|
|
|
39
44
|
this.query = new Query()
|
|
40
45
|
|
|
41
46
|
const businessData = await this.business.hydrate()
|
|
47
|
+
|
|
48
|
+
if (config.push !== false && businessData?.push?.public_key && Push.supported) {
|
|
49
|
+
this.push = new Push(businessData.push)
|
|
50
|
+
|
|
51
|
+
this.push.initialize().catch(error => {
|
|
52
|
+
console.warn('Hellotext Push initialization failed:', error)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
const webchatConfig =
|
|
43
57
|
config.webchat === false
|
|
44
58
|
? false
|
package/src/models/business.js
CHANGED
|
@@ -27,6 +27,7 @@ const stylesheetLoadTimeout = 10000
|
|
|
27
27
|
* @property {String} [locale] - Default dashboard locale for the business.
|
|
28
28
|
* @property {String} [style_url] - Stylesheet URL to inject for dashboard-managed surfaces.
|
|
29
29
|
* @property {BusinessWebchat|null} [webchat] - Dashboard webchat defaults.
|
|
30
|
+
* @property {{public_key: String}|null} [push] - Public VAPID key for push subscriptions.
|
|
30
31
|
* @property {String|Array<String>} [whitelist] - Domain whitelist configuration.
|
|
31
32
|
* @property {String} [subscription] - Current business subscription tier.
|
|
32
33
|
*/
|
package/src/models/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { Fingerprint } from './fingerprint'
|
|
|
4
4
|
export { Form } from './form'
|
|
5
5
|
export { FormCollection } from './form_collection'
|
|
6
6
|
export { Page } from './page'
|
|
7
|
+
export { Push } from './push'
|
|
7
8
|
export { Query } from './query'
|
|
8
9
|
export { Session } from './session'
|
|
9
10
|
export { User } from './user'
|