@subiz/widget-v4 4.1.210 → 4.1.211

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.
Files changed (124) hide show
  1. package/README.md +21 -2
  2. package/assets/less/chatbox.less +9 -0
  3. package/assets/less/form.less +10 -0
  4. package/assets/less/message.less +114 -13
  5. package/assets/less/widget.less +21 -16
  6. package/compiled_src/call/css.js +1 -1
  7. package/compiled_src/chatbox/Buttons.js +7 -4
  8. package/compiled_src/chatbox/ChatButton.js +115 -29
  9. package/compiled_src/chatbox/Event.js +37 -5
  10. package/compiled_src/chatbox/Events.js +348 -129
  11. package/compiled_src/chatbox/FormAttachment.js +1 -1
  12. package/compiled_src/chatbox/Header.js +6 -2
  13. package/compiled_src/chatbox/InfoMessage.js +1 -1
  14. package/compiled_src/chatbox/MessageInput.js +24 -9
  15. package/compiled_src/chatbox/OrderAttachments.js +13 -13
  16. package/compiled_src/chatbox/QuoteMessage.js +3 -2
  17. package/compiled_src/chatbox/TextMessage.js +22 -18
  18. package/compiled_src/chatbox/Widget.js +200 -129
  19. package/compiled_src/chatbox/block.js +39 -29
  20. package/compiled_src/chatbox/conversation-history.js +44 -0
  21. package/compiled_src/chatbox/event-layout.js +59 -0
  22. package/compiled_src/chatbox/greeting.js +32 -0
  23. package/compiled_src/common.js +13 -9
  24. package/compiled_src/form/css.js +1 -1
  25. package/compiled_src/lib.js +22 -32
  26. package/compiled_src/notif/css.js +1 -1
  27. package/compiled_src/plugin.js +11 -18
  28. package/compiled_src/popup/css.js +1 -1
  29. package/compiled_src/popup/popup.js +1 -1
  30. package/compiled_src/store/api.js +0 -2
  31. package/compiled_src/store/api_fake.js +1 -1
  32. package/compiled_src/store/attachments.js +28 -0
  33. package/compiled_src/store/chain.js +24 -10
  34. package/compiled_src/store/kv.js +9 -13
  35. package/compiled_src/store/lsdb.js +1 -1
  36. package/compiled_src/store/store.js +356 -117
  37. package/compiled_src/util/common.js +48 -41
  38. package/compiled_src/util/event.js +15 -0
  39. package/compiled_src/util/pubsub.js +28 -13
  40. package/compiled_src/util/url.js +18 -0
  41. package/dist/.HASH +1 -0
  42. package/dist/127.js +13 -13
  43. package/dist/132.js +1 -1
  44. package/dist/1a0ddc53cbb.127.js +1 -0
  45. package/dist/{1a0d80f6e72.132.js → 1a0ddc53cbb.132.js} +1 -1
  46. package/dist/1a0ddc53cbb.278.js +1 -0
  47. package/dist/{1a0d80f6e72.339.js → 1a0ddc53cbb.339.js} +1 -1
  48. package/dist/1a0ddc53cbb.app.js +1 -0
  49. package/dist/{1a0d80f6e72.chat.html → 1a0ddc53cbb.chat.html} +1 -1
  50. package/dist/1a0ddc53cbb.chat.js +1 -0
  51. package/dist/{1a0d80f6e72.form.html → 1a0ddc53cbb.form.html} +1 -1
  52. package/dist/1a0ddc53cbb.form.js +1 -0
  53. package/dist/{1a0d80f6e72.index.html → 1a0ddc53cbb.index.html} +1 -1
  54. package/dist/{1a0d80f6e72.library.html → 1a0ddc53cbb.library.html} +1 -1
  55. package/dist/1a0ddc53cbb.library.js +1 -0
  56. package/dist/{1a0d80f6e72.preview.html → 1a0ddc53cbb.preview.html} +1 -1
  57. package/dist/1a0ddc53cbb.preview.js +1 -0
  58. package/dist/1a0ddc53cbb.webview.js +1 -0
  59. package/dist/screenshoot.js +1492 -616
  60. package/package.json +10 -3
  61. package/src/chatbox/Buttons.js +5 -4
  62. package/src/chatbox/ChatButton.js +120 -39
  63. package/src/chatbox/Event.js +38 -5
  64. package/src/chatbox/Events.js +306 -134
  65. package/src/chatbox/FormAttachment.js +1 -1
  66. package/src/chatbox/Header.js +4 -4
  67. package/src/chatbox/InfoMessage.js +1 -1
  68. package/src/chatbox/MessageInput.js +15 -10
  69. package/src/chatbox/OrderAttachments.js +13 -13
  70. package/src/chatbox/QuoteMessage.js +3 -2
  71. package/src/chatbox/TextMessage.js +21 -20
  72. package/src/chatbox/Widget.js +150 -150
  73. package/src/chatbox/block.js +29 -27
  74. package/src/chatbox/conversation-history.js +37 -0
  75. package/src/chatbox/event-layout.js +72 -0
  76. package/src/chatbox/greeting.js +13 -0
  77. package/src/common.js +15 -9
  78. package/src/lib.js +22 -35
  79. package/src/plugin.js +11 -13
  80. package/src/popup/popup.js +1 -1
  81. package/src/store/api.js +0 -2
  82. package/src/store/api_fake.js +1 -1
  83. package/src/store/attachments.js +21 -0
  84. package/src/store/chain.js +25 -12
  85. package/src/store/kv.js +10 -19
  86. package/src/store/lsdb.js +1 -1
  87. package/src/store/store.js +313 -130
  88. package/src/util/common.js +48 -43
  89. package/src/util/event.js +7 -0
  90. package/src/util/pubsub.js +27 -8
  91. package/src/util/url.js +14 -0
  92. package/test.cjs +59 -0
  93. package/assets/less/contact.less +0 -126
  94. package/compiled_src/util/common.test.js +0 -414
  95. package/compiled_src/util/event.test.js +0 -38
  96. package/compiled_src/util/extractor.test.js +0 -299
  97. package/compiled_src/util/extractor.test2.js +0 -61
  98. package/compiled_src/util/gate.test.js +0 -18
  99. package/compiled_src/util/pubsub.test.js +0 -35
  100. package/dist/1a0d80f6e72.127.js +0 -1
  101. package/dist/1a0d80f6e72.278.js +0 -1
  102. package/dist/1a0d80f6e72.74.js +0 -1
  103. package/dist/1a0d80f6e72.997.js +0 -1
  104. package/dist/1a0d80f6e72.app.js +0 -1
  105. package/dist/1a0d80f6e72.chat.js +0 -1
  106. package/dist/1a0d80f6e72.form.js +0 -1
  107. package/dist/1a0d80f6e72.library.js +0 -1
  108. package/dist/1a0d80f6e72.preview.js +0 -1
  109. package/dist/1a0d80f6e72.webview.js +0 -1
  110. package/dist/74.js +0 -376
  111. package/dist/997.js +0 -37
  112. package/dist/HASH +0 -1
  113. package/src/contact/contact.js +0 -400
  114. package/src/contact/css.js +0 -1
  115. package/src/util/common.test.js +0 -301
  116. package/src/util/event.test.js +0 -31
  117. package/src/util/extractor.test.js +0 -240
  118. package/src/util/extractor.test2.js +0 -56
  119. package/src/util/gate.test.js +0 -20
  120. package/src/util/pubsub.test.js +0 -37
  121. /package/dist/{1a0d80f6e72.185.js → 1a0ddc53cbb.185.js} +0 -0
  122. /package/dist/{1a0d80f6e72.27.js → 1a0ddc53cbb.27.js} +0 -0
  123. /package/dist/{1a0d80f6e72.459.js → 1a0ddc53cbb.459.js} +0 -0
  124. /package/dist/{1a0d80f6e72.490.js → 1a0ddc53cbb.490.js} +0 -0
@@ -11,6 +11,7 @@ var _chain = _interopRequireDefault(require("./chain.js"));
11
11
  var _kv = _interopRequireDefault(require("./kv.js"));
12
12
  var _api_fake = _interopRequireDefault(require("./api_fake.js"));
13
13
  var _realtime_fake = _interopRequireDefault(require("./realtime_fake.js"));
14
+ var _attachments = require("./attachments.js");
14
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
16
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
16
17
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
@@ -24,7 +25,6 @@ cookie.defaults.expires = 3650;
24
25
  let memlocalStorage = require('./inmem_localstorage.js');
25
26
  const MAX_CONVO = 20;
26
27
  const page_loaded = _index.default.Date.now();
27
- const DEBUG_ACCOUNT_IDS = ['acqsulrowbxiugvginhw', 'acryguzgwxmamhzmqlyx'];
28
28
  class Store {
29
29
  constructor(ApiURL, FileURL, RealtimeURL, inmem, fakeapi) {
30
30
  _defineProperty(this, "inmem", false);
@@ -95,14 +95,23 @@ class Store {
95
95
  if (this._die) return dieErr;
96
96
  await this.readyGate.entry();
97
97
  await this.reconnectGate.entry(undefined, true);
98
- if (this.isInsync(cid)) return this.reconnectGate.open();
99
- this.markSync(cid);
100
- let {
101
- body: convo,
102
- error
103
- } = await this.api.getConversation(cid);
104
- if (convo && !error) this._setConvo(convo);
105
- return this.reconnectGate.open();
98
+ try {
99
+ if (this._die || this.isInsync(cid)) return;
100
+ this.convoFetchAfter = this.convoFetchAfter || {};
101
+ if (_index.default.Date.now() < (this.convoFetchAfter[cid] || 0)) return;
102
+ this.convoFetchAfter[cid] = _index.default.Date.now() + 15_000;
103
+ let {
104
+ body: convo,
105
+ error
106
+ } = await this.api.getConversation(cid);
107
+ if (convo && !error) this._setConvo(convo);
108
+ } catch (error) {
109
+ return {
110
+ error
111
+ };
112
+ } finally {
113
+ this.reconnectGate.open();
114
+ }
106
115
  });
107
116
  _defineProperty(this, "list_provinces", () => this.api.list_provinces());
108
117
  _defineProperty(this, "list_districts", p => this.api.list_districts(p));
@@ -113,25 +122,48 @@ class Store {
113
122
  if (id.startsWith('cslight')) return _index.default.get(this.lightConvos, [id, 'events'], []);
114
123
  return this.chain.listM(id);
115
124
  });
116
- _defineProperty(this, "fetchMoreEvents", async convoid => {
117
- if (!convoid) return;
118
- if (convoid.startsWith('cslight')) return [[], ''];
119
- if (!this.isInsync('events' + convoid)) {
120
- await this.readyGate.entry();
121
- await this.reconnectGate.entry(undefined, true);
122
- if (this.isInsync('events' + convoid)) {
123
- this.reconnectGate.open();
124
- return {};
125
+ _defineProperty(this, "fetchMoreEvents", (cid, limit = 150) => {
126
+ this.historyRequests = this.historyRequests || new Map();
127
+ if (this.historyRequests.has(cid)) return this.historyRequests.get(cid);
128
+ this.historyFailures = this.historyFailures || {};
129
+ const failure = this.historyFailures[cid];
130
+ if (failure && _index.default.Date.now() < failure.retryAt) return Promise.resolve({
131
+ error: failure.error
132
+ });
133
+ const request = this._fetchMoreEvents(cid, limit).then(result => {
134
+ if (result.error) this.historyFailures[cid] = {
135
+ error: result.error,
136
+ retryAt: _index.default.Date.now() + 15_000
137
+ };else delete this.historyFailures[cid];
138
+ return result;
139
+ }).finally(() => {
140
+ if (this.historyRequests.get(cid) === request) this.historyRequests.delete(cid);
141
+ });
142
+ this.historyRequests.set(cid, request);
143
+ return request;
144
+ });
145
+ _defineProperty(this, "syncConvoEvents", cid => {
146
+ if (this._die || !cid || cid.startsWith('cslight')) return Promise.resolve({});
147
+ this.messageSyncRequests = this.messageSyncRequests || new Map();
148
+ if (this.messageSyncRequests.has(cid)) return this.messageSyncRequests.get(cid);
149
+ this.messageSyncNextAt = this.messageSyncNextAt || {};
150
+ if (_index.default.Date.now() < (this.messageSyncNextAt[cid] || 0)) return Promise.resolve({
151
+ skipped: true
152
+ });
153
+ const chain = this.chain;
154
+ const request = this._syncConvoEvents(cid, chain).then(result => {
155
+ if (chain === this.chain && !this._die) {
156
+ this.messageSyncFailures = this.messageSyncFailures || {};
157
+ const failures = result.error ? Math.min((this.messageSyncFailures[cid] || 0) + 1, 4) : 0;
158
+ this.messageSyncFailures[cid] = failures;
159
+ this.messageSyncNextAt[cid] = _index.default.Date.now() + (failures ? 15_000 * 2 ** (failures - 1) : 15_000);
125
160
  }
126
- this.markSync('events' + convoid);
127
- await this.chain.reload(convoid, () => {
128
- return [[], ''];
129
- });
130
- let out = await this._doFetchMsg(convoid);
131
- this.reconnectGate.open();
132
- return out;
133
- }
134
- return this._doFetchMsg(convoid);
161
+ return result;
162
+ }).finally(() => {
163
+ if (this.messageSyncRequests.get(cid) === request) this.messageSyncRequests.delete(cid);
164
+ });
165
+ this.messageSyncRequests.set(cid, request);
166
+ return request;
135
167
  });
136
168
  _defineProperty(this, "reloadConvoEvents", async convoid => {
137
169
  await this.chain.reload(convoid, () => {
@@ -148,10 +180,10 @@ class Store {
148
180
  error
149
181
  };
150
182
  });
151
- _defineProperty(this, "_doFetchMsg", async convoid => {
183
+ _defineProperty(this, "_doFetchMsg", async (convoid, limit = 150) => {
152
184
  let {
153
185
  error
154
- } = await this.chain.fetchMore(convoid, 150, async (type, anchor, limit) => {
186
+ } = await this.chain.fetchMore(convoid, limit, async (type, anchor, limit) => {
155
187
  if (this.outofmsgM[convoid]) return [[], ''];
156
188
  let [events, newanchor, error, outofmsg] = await this.listmessageapi(type, anchor, limit);
157
189
  if (outofmsg) this.outofmsgM[convoid] = true;
@@ -190,17 +222,6 @@ class Store {
190
222
  _defineProperty(this, "now", () => _index.default.Date.now() + this.timeDiff);
191
223
  _defineProperty(this, "seenQueue", []);
192
224
  _defineProperty(this, "matchTrafficSource", () => this.kv.match('traffic_source'));
193
- _defineProperty(this, "sendBugReport", str => this.api.sendBugReport(str));
194
- _defineProperty(this, "matchDebugAccountIds", () => DEBUG_ACCOUNT_IDS);
195
- _defineProperty(this, "sbzListConversations", () => {
196
- return this.api.listConversations();
197
- });
198
- _defineProperty(this, "sbzListMessages", cid => {
199
- return this.api.listMessages(cid, 100, '');
200
- });
201
- _defineProperty(this, "listAgentRealtimeMessageSent", () => {
202
- return this.agent_rt_message_sent_dict;
203
- });
204
225
  _defineProperty(this, "_alreadyReceived", {});
205
226
  _defineProperty(this, "_typing", _index.default.throttle(convoid => this.api.sendTyping(convoid), 3000, {
206
227
  trailing: false
@@ -236,7 +257,6 @@ class Store {
236
257
  this.CONNECTION_CHECK_INTERVAL = 3000;
237
258
  this.reconnectTimestamp = 0;
238
259
  this.g_sync = {};
239
- this.agent_rt_message_sent_dict = {};
240
260
  this._die = false;
241
261
  this.forceDevice = null;
242
262
  this.forcePlugins = {};
@@ -268,15 +288,22 @@ class Store {
268
288
  this.getLocaleContent = this.api.getLocaleContent;
269
289
  this.g_haveSentSeenM = {};
270
290
  this.last_focused = this.now();
271
- window.addEventListener('blur', () => {
291
+ this._onBlur = () => {
292
+ if (this._die) return;
272
293
  this.last_focused = this.now();
273
294
  this.sendPresence();
274
- });
275
- window.addEventListener('focus', () => {
295
+ };
296
+ this._onFocus = () => {
297
+ if (this._die) return;
276
298
  this.last_focused = 0;
277
299
  this.sendPresence();
278
- });
279
- window.addEventListener('beforeunload', () => this.api.sendLostFocus());
300
+ };
301
+ this._onBeforeUnload = () => {
302
+ if (!this._die) this.api.sendLostFocus();
303
+ };
304
+ window.addEventListener('blur', this._onBlur);
305
+ window.addEventListener('focus', this._onFocus);
306
+ window.addEventListener('beforeunload', this._onBeforeUnload);
280
307
  this._lastPresenceURL = window.location.href;
281
308
  this._urlCheckInterval = _index.default.setInterval(() => {
282
309
  if (this._die) return _index.default.clearInterval(this._urlCheckInterval);
@@ -303,19 +330,20 @@ class Store {
303
330
  }
304
331
  this.sendPresence();
305
332
  }, 60000);
306
- onVisibilityChange(val => {
333
+ this._visibilityCleanup = onVisibilityChange(val => {
307
334
  if (this._die) return;
308
335
  this.browserVisibility = val;
309
336
  this.sendPresence();
310
337
  });
311
338
  this.loopTime();
312
339
  this.device = this.getDeviceInfo();
313
- const _gaInterval = _index.default.setInterval(() => {
340
+ this._gaInterval = _index.default.setInterval(() => {
341
+ if (this._die) return;
314
342
  let user = this.cache.match('user') || {};
315
343
  if (!user.id) return;
316
344
  var dev = this.getDeviceInfo();
317
345
  if (!dev.ga_tracking_ids || !dev.ga_client_id) return;
318
- _index.default.clearInterval(_gaInterval);
346
+ _index.default.clearInterval(this._gaInterval);
319
347
  this.device = dev;
320
348
  let ga_client_id = dev.ga_client_id;
321
349
  if (ga_client_id !== _index.default.getUserAttr(user, 'ga_client_id', 'text')) {
@@ -343,8 +371,28 @@ class Store {
343
371
  console.log('publish connectionStatus', this.connectionStatus);
344
372
  this.pubsub.publish('connection');
345
373
  }
374
+ if (this.connectionStatus === 'active' && (this._needsMessageSync || currentStatus !== 'active')) {
375
+ this._needsMessageSync = false;
376
+ this.syncKnownConvoEvents();
377
+ }
346
378
  }
347
379
  }
380
+ syncKnownConvoEvents() {
381
+ if (this._die) return Promise.resolve();
382
+ if (this.knownConvoSyncRequest) return this.knownConvoSyncRequest;
383
+ const chain = this.chain;
384
+ const request = (async () => {
385
+ const convos = _index.default.filter(this.cache.all(), (value, key) => key.startsWith('convo_'));
386
+ for (const convo of convos) {
387
+ if (this._die || chain !== this.chain) break;
388
+ await this.syncConvoEvents(convo.id);
389
+ }
390
+ })().finally(() => {
391
+ if (this.knownConvoSyncRequest === request) this.knownConvoSyncRequest = null;
392
+ });
393
+ this.knownConvoSyncRequest = request;
394
+ return request;
395
+ }
348
396
  linkProduct(p) {
349
397
  if (!p) {
350
398
  this._currentViewingProduct = {};
@@ -488,7 +536,90 @@ class Store {
488
536
  async _touchAcc() {
489
537
  if (this.isInsync('account')) return;
490
538
  await this.readyGate.entry();
491
- if (!this.isInsync('account')) this.reconnect();
539
+ if (!this.isInsync('account')) this.refreshAccount();
540
+ }
541
+ refreshAccount() {
542
+ if (this._die || _index.default.Date.now() < (this.accountRefreshAfter || 0)) return;
543
+ this.accountRefreshAfter = _index.default.Date.now() + 30_000;
544
+ return this.reconnect();
545
+ }
546
+ async _fetchMoreEvents(convoid, limit = 150) {
547
+ if (!convoid || convoid.startsWith('cslight')) return {};
548
+ await this.readyGate.entry();
549
+ await this.reconnectGate.entry(undefined, true);
550
+ try {
551
+ if (this._die) return dieErr;
552
+ const refresh = !this.isInsync('events' + convoid);
553
+ if (refresh) {
554
+ this.chain.anchorCache[convoid] = '';
555
+ delete this.outofmsgM[convoid];
556
+ }
557
+ const out = await this._doFetchMsg(convoid, limit);
558
+ if (!out.error) this.markSync('events' + convoid);
559
+ return out;
560
+ } catch (error) {
561
+ return {
562
+ error
563
+ };
564
+ } finally {
565
+ this.reconnectGate.open();
566
+ }
567
+ }
568
+ async _syncConvoEvents(cid, chain) {
569
+ await this.readyGate.entry();
570
+ await this.reconnectGate.entry(undefined, true);
571
+ try {
572
+ if (this._die || chain !== this.chain) return dieErr;
573
+ this.messageSyncTimes = this.messageSyncTimes || {};
574
+ this.messageSyncProgress = this.messageSyncProgress || {};
575
+ let progress = this.messageSyncProgress[cid];
576
+ if (!progress) {
577
+ const cached = Object.values(chain.listM(cid));
578
+ const boundary = this.messageSyncTimes[cid] || cached.reduce((min, ev) => Math.min(min, _index.default.getMs(ev.created)), Infinity);
579
+ progress = this.messageSyncProgress[cid] = {
580
+ boundary,
581
+ anchor: '',
582
+ newest: 0,
583
+ visited: new Set()
584
+ };
585
+ }
586
+ for (let page = 0; page < 5; page++) {
587
+ const wait = (this.nextMessageSyncRequestAt || 0) - _index.default.Date.now();
588
+ if (wait > 0) await _index.default.sleep(wait);
589
+ if (this._die || chain !== this.chain) return dieErr;
590
+ this.nextMessageSyncRequestAt = _index.default.Date.now() + 250;
591
+ const [items, next, error, end] = await this.listmessageapi(cid, progress.anchor, 150);
592
+ if (this._die || chain !== this.chain) return dieErr;
593
+ if (error) return {
594
+ error
595
+ };
596
+ chain.merge(cid, items);
597
+ for (const item of items) progress.newest = Math.max(progress.newest, item.index);
598
+ this.pubsub.publish('account');
599
+ if (end || !items.length || items.some(item => item.index < progress.boundary)) {
600
+ if (progress.newest) this.messageSyncTimes[cid] = progress.newest;
601
+ delete this.messageSyncProgress[cid];
602
+ return {};
603
+ }
604
+ progress.visited.add(progress.anchor);
605
+ if (!next || progress.visited.has(next)) {
606
+ delete this.messageSyncProgress[cid];
607
+ return {
608
+ error: 'message_cursor_stalled'
609
+ };
610
+ }
611
+ progress.anchor = next;
612
+ }
613
+ return {
614
+ pending: true
615
+ };
616
+ } catch (error) {
617
+ return {
618
+ error
619
+ };
620
+ } finally {
621
+ this.reconnectGate.open();
622
+ }
492
623
  }
493
624
  async init(accid, mask, userRef, options) {
494
625
  this.g_sync = {};
@@ -569,7 +700,6 @@ class Store {
569
700
  }
570
701
  async _initResource(accid, auth, realtimep) {
571
702
  this.g_sync = {};
572
- this._current_acc_id = accid;
573
703
  let user = await this.api.setAuth(auth);
574
704
  if (typeof sbz_info != 'undefined') sbz_info.store_7 = true;
575
705
  if (user.error) {
@@ -581,6 +711,7 @@ class Store {
581
711
  let lightConvos = this.kv.match('light_convos') || {};
582
712
  this.lightConvos = {};
583
713
  _index.default.map(lightConvos, convo => {
714
+ if (convo.light_type === 'widget' && (convo.account_id !== accid || convo.initial_by !== user.id)) return;
584
715
  let in7Day = convo.created > _index.default.Date.now() - 7 * 24 * 3_600_000;
585
716
  let events = _index.default.filter(convo.events, ev => ev.type === 'message_sent');
586
717
  let messages = _index.default.map(events, ev => _index.default.get(ev, 'data.message') || {});
@@ -594,8 +725,10 @@ class Store {
594
725
  if (this.fake) this.realtime = new _realtime_fake.default();else this.realtime = new Realtime(this.RealtimeURL, realtimep, xhrsend, accid);
595
726
  this.realtime.onEvent(this.onWs.bind(this));
596
727
  this.realtime.onInterrupted(async () => {
728
+ if (this._die) return;
729
+ this._needsMessageSync = true;
597
730
  await this.reconnectWs(accid, user.id);
598
- this.reconnect();
731
+ await this.refreshAccount();
599
732
  });
600
733
  if (typeof sbz_info != 'undefined') sbz_info.store_8 = true;
601
734
  this.seenLoop();
@@ -643,7 +776,7 @@ class Store {
643
776
  var lastmsgsent = events[_index.default.size(events) - 1];
644
777
  if (!lastmsgsent) return;
645
778
  if (this.g_haveSentSeenM[lastmsgsent.id]) return;
646
- if (lastmsgsent.id === lastmsgsent.data.message.idempotency_key) return;
779
+ if (lastmsgsent.id === lastmsgsent.source_event_id) return;
647
780
  this.pubsub.publish('events', {
648
781
  created: this.now(),
649
782
  type: 'message_pong',
@@ -685,34 +818,52 @@ class Store {
685
818
  await this.realtime.subscribe([`conversation_state_updated.account.${accid}.user.${userid}`, `conversation_joined.account.${accid}.user.${userid}`, `conversation_typing.account.${accid}.user.${userid}`, `message_sent.account.${accid}.user.${userid}`, `message_pong.account.${accid}.user.${userid}`, `conversation_rating_requested.account.${accid}.user.${userid}`, `conversation_rated.account.${accid}.user.${userid}`, `conversation_events.account.${accid}.user.${userid}`]);
686
819
  await this.readyGate.entry();
687
820
  }
688
- async reconnect(p) {
821
+ reconnect(p) {
822
+ if (this.reconnectRequest) return this.reconnectRequest;
823
+ const request = this._reconnect(p).finally(() => {
824
+ if (this.reconnectRequest === request) this.reconnectRequest = null;
825
+ });
826
+ this.reconnectRequest = request;
827
+ return request;
828
+ }
829
+ async _reconnect(p) {
689
830
  if (this._die) return;
690
831
  await this.reconnectGate.entry(undefined, true);
691
- this.markSync('account');
692
- let fetchuser = p && p.user ? Promise.resolve({
693
- body: p.user
694
- }) : this.api.getUser();
695
- let outs = await Promise.all([fetchuser, this.api.getAccountSetting({
696
- bot_id: this.forceBotId
697
- })]);
698
- let error = _index.default.find(outs, out => out.error);
699
- if (error) return console.error(error);
700
- let set = outs[1].body;
701
- set.user = outs[0].body;
702
- await this._setSetting(set);
703
- let convob = await this.api.listConversations();
704
- error = convob.error;
705
- let body = convob.body;
706
- if (!error) {
707
- let convos = _index.default.get(body, 'conversations', []);
708
- _index.default.map(this.cache.all(), (value, k) => {
709
- if (k.startsWith('convo_')) {
710
- if (this.now() - _index.default.getMs(_index.default.get(value, 'actived')) > 1209600000) this.cache.del(k);
711
- }
712
- });
713
- _index.default.map(convos, convo => this._setConvo(convo));
832
+ try {
833
+ let fetchuser = p && p.user ? Promise.resolve({
834
+ body: p.user
835
+ }) : this.api.getUser();
836
+ let outs = await Promise.all([fetchuser, this.api.getAccountSetting({
837
+ bot_id: this.forceBotId
838
+ })]);
839
+ let error = _index.default.find(outs, out => out.error);
840
+ if (error) return error;
841
+ let set = outs[1].body;
842
+ set.user = outs[0].body;
843
+ await this._setSetting(set);
844
+ let convob = await this.api.listConversations();
845
+ error = convob.error;
846
+ let body = convob.body;
847
+ if (!error) {
848
+ let convos = _index.default.get(body, 'conversations', []);
849
+ _index.default.map(this.cache.all(), (value, k) => {
850
+ if (k.startsWith('convo_')) {
851
+ if (this.now() - _index.default.getMs(_index.default.get(value, 'actived')) > 1209600000) this.cache.del(k);
852
+ }
853
+ });
854
+ _index.default.map(convos, convo => this._setConvo(convo));
855
+ }
856
+ if (error) return {
857
+ error
858
+ };
859
+ this.markSync('account');
860
+ } catch (error) {
861
+ return {
862
+ error
863
+ };
864
+ } finally {
865
+ this.reconnectGate.open();
714
866
  }
715
- return this.reconnectGate.open();
716
867
  }
717
868
  async submitForm(formid, data, user) {
718
869
  await this.readyGate.entry();
@@ -826,20 +977,19 @@ class Store {
826
977
  return this.api.sendEvent(e);
827
978
  }
828
979
  async onWs(ev) {
829
- await this.wsGate.entry();
830
- this.wsGate.close();
831
- await this._onWs(ev);
832
- this.wsGate.open();
833
- }
834
- handleLogCompare(ev) {
835
- if (!DEBUG_ACCOUNT_IDS.includes(this._current_acc_id)) return;
836
- if (ev.type !== 'message_sent') return;
837
- let byType = _index.default.get(ev, 'by.type');
838
- if (byType === 'user') return;
839
- this.agent_rt_message_sent_dict[ev.id] = ev;
980
+ await this.wsGate.entry(undefined, true);
981
+ try {
982
+ await this._onWs(ev);
983
+ } catch (error) {
984
+ console.error(error);
985
+ return {
986
+ error
987
+ };
988
+ } finally {
989
+ this.wsGate.open();
990
+ }
840
991
  }
841
992
  async _onWs(ev) {
842
- this.handleLogCompare(ev);
843
993
  if (this._die) return;
844
994
  if (!ev || !ev.type) return;
845
995
  ev.created = _index.default.getMs(ev.created);
@@ -908,7 +1058,7 @@ class Store {
908
1058
  }
909
1059
  }
910
1060
  if (_index.default.startsWith(ev.type, 'message_') || _index.default.startsWith(ev.type, 'conversation_')) {
911
- this.restConvoEvent(ev);
1061
+ await this.restConvoEvent(ev);
912
1062
  if (ev.type === 'message_sent' && _index.default.get(ev, 'by.id') !== user.id) {
913
1063
  sendEventToThirdParty('SUBIZ Chat', 'Received', 'User');
914
1064
  }
@@ -957,8 +1107,8 @@ class Store {
957
1107
  }
958
1108
  if (ev.type !== 'message_pong' && ev.type !== 'conversation_typing') {
959
1109
  if (ev.type === 'message_sent') {
960
- let idemkey = _index.default.get(ev, 'data.message.idempotency_key');
961
- let found = _index.default.find(this.listEvents(cid), oldev => oldev.id === idemkey);
1110
+ let sourceEventId = _index.default.get(ev, 'source_event_id');
1111
+ let found = _index.default.find(this.listEvents(cid), oldev => oldev.id === sourceEventId);
962
1112
  if (found) {
963
1113
  this.chain.del(cid, found.id);
964
1114
  ev.created = found.created;
@@ -984,6 +1134,58 @@ class Store {
984
1134
  delete this.lightConvos[id];
985
1135
  this.kv.put('light_convos', this.lightConvos);
986
1136
  }
1137
+ startWidgetConvo(greeting, locale) {
1138
+ const user = this.matchUser();
1139
+ const existing = _index.default.find(this.lightConvos, convo => convo.light_type === 'widget' && convo.account_id === user.account_id && convo.initial_by === user.id);
1140
+ if (existing) return {
1141
+ convo: existing
1142
+ };
1143
+ const id = 'cslightwidget_' + _index.default.randomString(24);
1144
+ const ev = _index.default.cloneDeep(greeting);
1145
+ ev.data.message.conversation_id = id;
1146
+ const convo = {
1147
+ id,
1148
+ account_id: user.account_id,
1149
+ initial_by: user.id,
1150
+ locale,
1151
+ light_type: 'widget',
1152
+ state: 'pending',
1153
+ created: ev.created,
1154
+ members: [{
1155
+ id: user.id,
1156
+ type: 'user',
1157
+ seen_at: ev.created + 1
1158
+ }],
1159
+ events: [ev],
1160
+ last_message_sent: ev,
1161
+ touchpoint: {
1162
+ id,
1163
+ channel: 'subiz',
1164
+ source: 'web'
1165
+ }
1166
+ };
1167
+ this.lightConvos[id] = convo;
1168
+ this.kv.put('light_convos', this.lightConvos);
1169
+ this.pubsub.publish('account');
1170
+ return {
1171
+ convo
1172
+ };
1173
+ }
1174
+ promoteWidgetConvo(localId, cid) {
1175
+ this.widgetConvoOrigins = this.widgetConvoOrigins || {};
1176
+ this.widgetConvoOrigins[cid] = localId;
1177
+ const greeting = _index.default.cloneDeep(_index.default.get(this.lightConvos, [localId, 'events', 0]));
1178
+ if (greeting) {
1179
+ greeting.source_event_id = greeting.id;
1180
+ greeting.data.message.conversation_id = cid;
1181
+ this.chain.merge(cid, [{
1182
+ id: greeting.id,
1183
+ index: greeting.created,
1184
+ value: greeting
1185
+ }]);
1186
+ }
1187
+ this.deleteLightConvo(localId);
1188
+ }
987
1189
  startLightConvo(botid, messages, locale, is_testing, botversion) {
988
1190
  let convoid = 'cslight' + botid + '_' + this.now();
989
1191
  let user = this.cache.match('user') || {};
@@ -1077,10 +1279,10 @@ class Store {
1077
1279
  }
1078
1280
  async sendConvoEvent(cid, type, data) {
1079
1281
  await this.readyGate.entry();
1080
- var idempotency = _index.default.randomString(36);
1282
+ var sourceEventId = _index.default.randomString(36);
1081
1283
  let user = this.cache.match('user') || {};
1082
1284
  var event = {
1083
- id: idempotency,
1285
+ id: sourceEventId,
1084
1286
  by: {
1085
1287
  id: user.id,
1086
1288
  type: 'user',
@@ -1093,7 +1295,7 @@ class Store {
1093
1295
  _index.default.convoToEv(cid, event);
1094
1296
  let files = [];
1095
1297
  if (type === 'message_sent') {
1096
- event.data.message.idempotency_key = idempotency;
1298
+ event.source_event_id = sourceEventId;
1097
1299
  event.data.message.conversation_id = cid;
1098
1300
  await flow.map(event.data.message.attachments, 2, async (att, i) => {
1099
1301
  if (att.type !== 'file') return;
@@ -1113,18 +1315,9 @@ class Store {
1113
1315
  }
1114
1316
  this.onWs(event);
1115
1317
  await this.convoGate.entry();
1116
- await flow.map(files, 1, async att => {
1117
- const {
1118
- body,
1119
- url,
1120
- error
1121
- } = await this.api.uploadToS3(att._file);
1122
- if (_index.default.get(body, 'code') == 'invalid_field_size') {
1123
- event._error_message = 'cannot_send_too_big_file';
1124
- }
1125
- event._error = error;
1126
- att.url = url;
1127
- });
1318
+ const uploaded = await (0, _attachments.uploadAttachments)(files, file => this.api.uploadToS3(file));
1319
+ event._error = uploaded.error;
1320
+ if (uploaded.errorMessage) event._error_message = uploaded.errorMessage;
1128
1321
  if (event._error) return this.onWs(event);
1129
1322
  if (cid && cid.startsWith('cslight')) return;
1130
1323
  let {
@@ -1233,13 +1426,20 @@ class Store {
1233
1426
  }
1234
1427
  async listmessageapi(type, anchor, limit) {
1235
1428
  let convoid = type;
1236
- if (convoid && convoid.startsWith('cslight')) return [fakeevents, '', undefined, true];
1429
+ if (convoid && convoid.startsWith('cslight')) {
1430
+ let events = _index.default.map(this.listEvents(convoid), ev => ({
1431
+ id: ev.id,
1432
+ index: ev.created,
1433
+ value: ev
1434
+ }));
1435
+ return [events, '', undefined, true];
1436
+ }
1237
1437
  let {
1238
1438
  body: events,
1239
1439
  code,
1240
1440
  error
1241
1441
  } = await this.api.listMessages(convoid, -limit, anchor);
1242
- if (error || code !== 200) return [[], anchor, error];
1442
+ if (error || code !== 200) return [[], anchor, error || 'message_fetch_failed'];
1243
1443
  anchor = _index.default.get(_index.default.last(events), 'id');
1244
1444
  events = _index.default.map(events, event => {
1245
1445
  event.created = _index.default.getMs(event.created);
@@ -1268,17 +1468,18 @@ class Store {
1268
1468
  async loopTime() {
1269
1469
  if (this._die) return;
1270
1470
  let startms = this.now();
1271
- if (!this.cache || !_index.default.get(this.cache.match('user'), 'id')) return _index.default.setTimeout(() => this.loopTime(), 240000);
1471
+ if (!this.cache || !_index.default.get(this.cache.match('user'), 'id')) return this._timeTimeout = _index.default.setTimeout(() => this.loopTime(), 240000);
1272
1472
  let {
1273
1473
  body: t,
1274
1474
  error
1275
1475
  } = await this.api.time();
1276
- if (error) return _index.default.setTimeout(() => this.loopTime(), 30000);
1476
+ if (this._die) return;
1477
+ if (error) return this._timeTimeout = _index.default.setTimeout(() => this.loopTime(), 30000);
1277
1478
  var latences = Math.abs(this.now() - startms) / 2;
1278
1479
  var serverMs = new _index.default.Date(t).getTime() + latences;
1279
1480
  var localMs = this.now();
1280
1481
  this.timeDiff = Math.floor(serverMs - localMs);
1281
- _index.default.setTimeout(() => this.loopTime(), 240000);
1482
+ this._timeTimeout = _index.default.setTimeout(() => this.loopTime(), 240000);
1282
1483
  }
1283
1484
  replaceFileUrl(src) {
1284
1485
  if (!src) return '';
@@ -1310,6 +1511,14 @@ class Store {
1310
1511
  this.kv = new _kv.default(new _lsdb.default('sbz2023', this.inmem), 'kv');
1311
1512
  if (this.chain) this.chain.destroy();
1312
1513
  this.chain = new _chain.default(new _lsdb.default('cbz2023', true), 'ch');
1514
+ this.outofmsgM = {};
1515
+ this.messageSyncTimes = {};
1516
+ this.messageSyncNextAt = {};
1517
+ this.messageSyncFailures = {};
1518
+ this.messageSyncProgress = {};
1519
+ this.accountRefreshAfter = 0;
1520
+ this.historyFailures = {};
1521
+ this.convoFetchAfter = {};
1313
1522
  if (this.realtime) this.realtime.stop();
1314
1523
  if (this.cache) this.cache.destroy();
1315
1524
  this.pubsub.publish('destroy');
@@ -1318,14 +1527,22 @@ class Store {
1318
1527
  return this._die;
1319
1528
  }
1320
1529
  destroy() {
1530
+ if (this._die) return;
1321
1531
  this._die = true;
1322
1532
  this.clearBrowserData();
1323
1533
  _index.default.clearInterval(this._seenInterval);
1324
1534
  _index.default.clearInterval(this._urlCheckInterval);
1535
+ _index.default.clearInterval(this._presenceInterval1);
1536
+ _index.default.clearInterval(this._presenceInterval2);
1537
+ _index.default.clearInterval(this._gaInterval);
1538
+ _index.default.clearTimeout(this._timeTimeout);
1325
1539
  if (this.realtime) this.realtime.stop();
1326
- this.readyGate.close();
1327
- window.removeEventListener('blur', this.sendPresence);
1328
- window.removeEventListener('focus', this.sendPresence);
1540
+ if (this.readyGate) this.readyGate.close();
1541
+ window.removeEventListener('blur', this._onBlur);
1542
+ window.removeEventListener('focus', this._onFocus);
1543
+ window.removeEventListener('beforeunload', this._onBeforeUnload);
1544
+ if (this._visibilityCleanup) this._visibilityCleanup();
1545
+ this.pubsub.clear();
1329
1546
  }
1330
1547
  }
1331
1548
  exports.default = Store;
@@ -1350,7 +1567,23 @@ function loadImage(file) {
1350
1567
  const ImageTypes = ['image/gif', 'image/jpeg', 'image/png', 'image/svg+xml'];
1351
1568
  function onVisibilityChange(cb) {
1352
1569
  var hidden = 'hidden';
1353
- if (hidden in document) document.addEventListener('visibilitychange', onchange);else if ((hidden = 'mozHidden') in document) document.addEventListener('mozvisibilitychange', onchange);else if ((hidden = 'webkitHidden') in document) document.addEventListener('webkitvisibilitychange', onchange);else if ((hidden = 'msHidden') in document) document.addEventListener('msvisibilitychange', onchange);else if ('onfocusin' in document) document.onfocusin = document.onfocusout = onchange;else window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange;
1570
+ let target = document,
1571
+ eventName;
1572
+ let previous = {};
1573
+ if (hidden in document) eventName = 'visibilitychange';else if ((hidden = 'mozHidden') in document) eventName = 'mozvisibilitychange';else if ((hidden = 'webkitHidden') in document) eventName = 'webkitvisibilitychange';else if ((hidden = 'msHidden') in document) eventName = 'msvisibilitychange';else if ('onfocusin' in document) {
1574
+ previous = {
1575
+ onfocusin: document.onfocusin,
1576
+ onfocusout: document.onfocusout
1577
+ };
1578
+ document.onfocusin = document.onfocusout = onchange;
1579
+ } else {
1580
+ target = window;
1581
+ for (const key of ['onpageshow', 'onpagehide', 'onfocus', 'onblur']) {
1582
+ previous[key] = window[key];
1583
+ window[key] = onchange;
1584
+ }
1585
+ }
1586
+ if (eventName) target.addEventListener(eventName, onchange);
1354
1587
  function onchange(evt) {
1355
1588
  var v = 'visible';
1356
1589
  var h = 'hidden';
@@ -1368,6 +1601,12 @@ function onVisibilityChange(cb) {
1368
1601
  if (document[hidden] !== undefined) onchange({
1369
1602
  type: document[hidden] ? 'blur' : 'focus'
1370
1603
  });
1604
+ return () => {
1605
+ if (eventName) target.removeEventListener(eventName, onchange);
1606
+ for (const key of Object.keys(previous)) {
1607
+ if (target[key] === onchange) target[key] = previous[key];
1608
+ }
1609
+ };
1371
1610
  }
1372
1611
  const inmemCookie = {
1373
1612
  data: {},