@knocklabs/client 0.8.15 → 0.8.17

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 (71) hide show
  1. package/README.md +9 -27
  2. package/dist/cjs/api.js +47 -56
  3. package/dist/cjs/api.js.map +1 -1
  4. package/dist/cjs/clients/feed/feed.js +531 -624
  5. package/dist/cjs/clients/feed/feed.js.map +1 -1
  6. package/dist/cjs/clients/feed/index.js +1 -10
  7. package/dist/cjs/clients/feed/index.js.map +1 -1
  8. package/dist/cjs/clients/feed/interfaces.js.map +1 -1
  9. package/dist/cjs/clients/feed/store.js +4 -15
  10. package/dist/cjs/clients/feed/store.js.map +1 -1
  11. package/dist/cjs/clients/feed/types.js.map +1 -1
  12. package/dist/cjs/clients/feed/utils.js +0 -5
  13. package/dist/cjs/clients/feed/utils.js.map +1 -1
  14. package/dist/cjs/clients/preferences/index.js +216 -249
  15. package/dist/cjs/clients/preferences/index.js.map +1 -1
  16. package/dist/cjs/clients/preferences/interfaces.js.map +1 -1
  17. package/dist/cjs/clients/users/index.js +134 -61
  18. package/dist/cjs/clients/users/index.js.map +1 -1
  19. package/dist/cjs/clients/users/interfaces.js +6 -0
  20. package/dist/cjs/clients/users/interfaces.js.map +1 -0
  21. package/dist/cjs/index.js +3 -23
  22. package/dist/cjs/index.js.map +1 -1
  23. package/dist/cjs/interfaces.js.map +1 -1
  24. package/dist/cjs/knock.js +9 -22
  25. package/dist/cjs/knock.js.map +1 -1
  26. package/dist/cjs/networkStatus.js +3 -6
  27. package/dist/cjs/networkStatus.js.map +1 -1
  28. package/dist/esm/api.js +19 -21
  29. package/dist/esm/api.js.map +1 -1
  30. package/dist/esm/clients/feed/feed.js +104 -148
  31. package/dist/esm/clients/feed/feed.js.map +1 -1
  32. package/dist/esm/clients/feed/index.js +0 -5
  33. package/dist/esm/clients/feed/index.js.map +1 -1
  34. package/dist/esm/clients/feed/interfaces.js.map +1 -1
  35. package/dist/esm/clients/feed/store.js +2 -8
  36. package/dist/esm/clients/feed/store.js.map +1 -1
  37. package/dist/esm/clients/feed/types.js.map +1 -1
  38. package/dist/esm/clients/feed/utils.js +0 -1
  39. package/dist/esm/clients/feed/utils.js.map +1 -1
  40. package/dist/esm/clients/preferences/index.js +35 -25
  41. package/dist/esm/clients/preferences/index.js.map +1 -1
  42. package/dist/esm/clients/preferences/interfaces.js.map +1 -1
  43. package/dist/esm/clients/users/index.js +47 -16
  44. package/dist/esm/clients/users/index.js.map +1 -1
  45. package/dist/esm/clients/users/interfaces.js +2 -0
  46. package/dist/esm/clients/users/interfaces.js.map +1 -0
  47. package/dist/esm/index.js.map +1 -1
  48. package/dist/esm/interfaces.js.map +1 -1
  49. package/dist/esm/knock.js +8 -23
  50. package/dist/esm/knock.js.map +1 -1
  51. package/dist/esm/networkStatus.js +3 -5
  52. package/dist/esm/networkStatus.js.map +1 -1
  53. package/dist/types/api.d.ts +3 -1
  54. package/dist/types/api.d.ts.map +1 -1
  55. package/dist/types/clients/feed/feed.d.ts +7 -1
  56. package/dist/types/clients/feed/feed.d.ts.map +1 -1
  57. package/dist/types/clients/feed/interfaces.d.ts +4 -1
  58. package/dist/types/clients/feed/interfaces.d.ts.map +1 -1
  59. package/dist/types/clients/feed/types.d.ts +10 -10
  60. package/dist/types/clients/feed/types.d.ts.map +1 -1
  61. package/dist/types/clients/preferences/index.d.ts +27 -0
  62. package/dist/types/clients/preferences/index.d.ts.map +1 -1
  63. package/dist/types/clients/preferences/interfaces.d.ts +7 -4
  64. package/dist/types/clients/preferences/interfaces.d.ts.map +1 -1
  65. package/dist/types/clients/users/index.d.ts +8 -9
  66. package/dist/types/clients/users/index.d.ts.map +1 -1
  67. package/dist/types/clients/users/interfaces.d.ts +8 -0
  68. package/dist/types/clients/users/interfaces.d.ts.map +1 -0
  69. package/dist/types/interfaces.d.ts +6 -2
  70. package/dist/types/interfaces.d.ts.map +1 -1
  71. package/package.json +16 -11
@@ -1,10 +1,7 @@
1
1
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
-
4
- 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; }
5
-
6
- 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; }
7
-
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
8
5
  import { EventEmitter2 as EventEmitter } from "eventemitter2";
9
6
  import createStore from "./store";
10
7
  import { isRequestInFlight, NetworkStatus } from "../../networkStatus";
@@ -12,27 +9,20 @@ import { isRequestInFlight, NetworkStatus } from "../../networkStatus";
12
9
  var feedClientDefaults = {
13
10
  archived: "exclude"
14
11
  };
15
-
12
+ var DEFAULT_DISCONNECT_DELAY = 2000;
16
13
  class Feed {
17
- // The raw store instance, used for binding in React and other environments
18
14
  constructor(knock, feedId, options) {
19
15
  this.knock = knock;
20
16
  this.feedId = feedId;
21
-
22
17
  _defineProperty(this, "apiClient", void 0);
23
-
24
18
  _defineProperty(this, "userFeedId", void 0);
25
-
26
19
  _defineProperty(this, "channel", void 0);
27
-
28
20
  _defineProperty(this, "broadcaster", void 0);
29
-
30
21
  _defineProperty(this, "defaultOptions", void 0);
31
-
32
22
  _defineProperty(this, "broadcastChannel", void 0);
33
-
23
+ _defineProperty(this, "disconnectTimer", null);
24
+ // The raw store instance, used for binding in React and other environments
34
25
  _defineProperty(this, "store", void 0);
35
-
36
26
  this.apiClient = knock.client();
37
27
  this.feedId = feedId;
38
28
  this.userFeedId = this.buildUserFeedId();
@@ -41,55 +31,55 @@ class Feed {
41
31
  wildcard: true,
42
32
  delimiter: "."
43
33
  });
44
- this.defaultOptions = _objectSpread(_objectSpread({}, feedClientDefaults), options); // In server environments we might not have a socket connection
34
+ this.defaultOptions = _objectSpread(_objectSpread({}, feedClientDefaults), options);
45
35
 
36
+ // In server environments we might not have a socket connection
46
37
  if (this.apiClient.socket) {
47
38
  this.channel = this.apiClient.socket.channel("feeds:".concat(this.userFeedId), this.defaultOptions);
48
39
  this.channel.on("new-message", resp => this.onNewMessageReceived(resp));
49
- } // Attempt to bind to listen to other events from this feed in different tabs
50
- // Note: here we ensure `self` is available (it's not in server rendered envs)
51
-
40
+ }
52
41
 
42
+ // Attempt to bind to listen to other events from this feed in different tabs
43
+ // Note: here we ensure `self` is available (it's not in server rendered envs)
53
44
  this.broadcastChannel = typeof self !== "undefined" && "BroadcastChannel" in self ? new BroadcastChannel("knock:feed:".concat(this.userFeedId)) : null;
45
+ if (options.auto_manage_socket_connection && this.apiClient.socket) {
46
+ this.setupAutoSocketManager(options.auto_manage_socket_connection_delay);
47
+ }
54
48
  }
49
+
55
50
  /**
56
51
  * Cleans up a feed instance by destroying the store and disconnecting
57
52
  * an open socket connection.
58
53
  */
59
-
60
-
61
54
  teardown() {
62
55
  if (this.channel) {
63
56
  this.channel.leave();
64
57
  this.channel.off("new-message");
65
58
  }
66
-
67
59
  this.broadcaster.removeAllListeners();
68
60
  this.store.destroy();
69
-
70
61
  if (this.broadcastChannel) {
71
62
  this.broadcastChannel.close();
72
63
  }
73
64
  }
65
+
74
66
  /*
75
67
  Initializes a real-time connection to Knock, connecting the websocket for the
76
68
  current ApiClient instance if the socket is not already connected.
77
69
  */
78
-
79
-
80
70
  listenForUpdates() {
81
71
  // Connect the socket only if we don't already have a connection
82
72
  if (this.apiClient.socket && !this.apiClient.socket.isConnected()) {
83
73
  this.apiClient.socket.connect();
84
- } // Only join the channel if we're not already in a joining state
85
-
74
+ }
86
75
 
76
+ // Only join the channel if we're not already in a joining state
87
77
  if (this.channel && ["closed", "errored"].includes(this.channel.state)) {
88
78
  this.channel.join();
89
- } // Opt into receiving updates from _other tabs for the same user / feed_ via the broadcast
90
- // channel (iff it's enabled and exists)
91
-
79
+ }
92
80
 
81
+ // Opt into receiving updates from _other tabs for the same user / feed_ via the broadcast
82
+ // channel (iff it's enabled and exists)
93
83
  if (this.broadcastChannel && this.defaultOptions.__experimentalCrossBrowserUpdates === true) {
94
84
  this.broadcastChannel.onmessage = e => {
95
85
  switch (e.data.type) {
@@ -107,45 +97,35 @@ class Feed {
107
97
  // maybe do this optimistically without the fetch.
108
98
  return this.fetch();
109
99
  break;
110
-
111
100
  default:
112
101
  return null;
113
102
  }
114
103
  };
115
104
  }
116
105
  }
117
- /* Binds a handler to be invoked when event occurs */
118
-
119
106
 
107
+ /* Binds a handler to be invoked when event occurs */
120
108
  on(eventName, callback) {
121
109
  this.broadcaster.on(eventName, callback);
122
110
  }
123
-
124
111
  off(eventName, callback) {
125
112
  this.broadcaster.off(eventName, callback);
126
113
  }
127
-
128
114
  getState() {
129
115
  return this.store.getState();
130
116
  }
131
-
132
117
  markAsSeen(itemOrItems) {
133
118
  var _this = this;
134
-
135
119
  return _asyncToGenerator(function* () {
136
120
  var now = new Date().toISOString();
137
-
138
121
  _this.optimisticallyPerformStatusUpdate(itemOrItems, "seen", {
139
122
  seen_at: now
140
123
  }, "unseen_count");
141
-
142
124
  return _this.makeStatusUpdate(itemOrItems, "seen");
143
125
  })();
144
126
  }
145
-
146
127
  markAllAsSeen() {
147
128
  var _this2 = this;
148
-
149
129
  return _asyncToGenerator(function* () {
150
130
  // To mark all of the messages as seen we:
151
131
  // 1. Optimistically update *everything* we have in the store
@@ -166,10 +146,11 @@ class Feed {
166
146
  metadata,
167
147
  items
168
148
  } = getState();
169
- var isViewingOnlyUnseen = _this2.defaultOptions.status === "unseen"; // If we're looking at the unseen view, then we want to remove all of the items optimistically
149
+ var isViewingOnlyUnseen = _this2.defaultOptions.status === "unseen";
150
+
151
+ // If we're looking at the unseen view, then we want to remove all of the items optimistically
170
152
  // from the store given that nothing should be visible. We do this by resetting the store state
171
153
  // and setting the current metadata counts to 0
172
-
173
154
  if (isViewingOnlyUnseen) {
174
155
  setState(store => store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
175
156
  total_count: 0,
@@ -185,52 +166,40 @@ class Feed {
185
166
  };
186
167
  var itemIds = items.map(item => item.id);
187
168
  setState(store => store.setItemAttrs(itemIds, attrs));
188
- } // Issue the API request to the bulk status change API
189
-
169
+ }
190
170
 
171
+ // Issue the API request to the bulk status change API
191
172
  var result = yield _this2.makeBulkStatusUpdate("seen");
192
-
193
173
  _this2.broadcaster.emit("items:all_seen", {
194
174
  items
195
175
  });
196
-
197
176
  _this2.broadcastOverChannel("items:all_seen", {
198
177
  items
199
178
  });
200
-
201
179
  return result;
202
180
  })();
203
181
  }
204
-
205
182
  markAsUnseen(itemOrItems) {
206
183
  var _this3 = this;
207
-
208
184
  return _asyncToGenerator(function* () {
209
185
  _this3.optimisticallyPerformStatusUpdate(itemOrItems, "unseen", {
210
186
  seen_at: null
211
187
  }, "unseen_count");
212
-
213
188
  return _this3.makeStatusUpdate(itemOrItems, "unseen");
214
189
  })();
215
190
  }
216
-
217
191
  markAsRead(itemOrItems) {
218
192
  var _this4 = this;
219
-
220
193
  return _asyncToGenerator(function* () {
221
194
  var now = new Date().toISOString();
222
-
223
195
  _this4.optimisticallyPerformStatusUpdate(itemOrItems, "read", {
224
196
  read_at: now
225
197
  }, "unread_count");
226
-
227
198
  return _this4.makeStatusUpdate(itemOrItems, "read");
228
199
  })();
229
200
  }
230
-
231
201
  markAllAsRead() {
232
202
  var _this5 = this;
233
-
234
203
  return _asyncToGenerator(function* () {
235
204
  // To mark all of the messages as read we:
236
205
  // 1. Optimistically update *everything* we have in the store
@@ -251,10 +220,11 @@ class Feed {
251
220
  metadata,
252
221
  items
253
222
  } = getState();
254
- var isViewingOnlyUnread = _this5.defaultOptions.status === "unread"; // If we're looking at the unread view, then we want to remove all of the items optimistically
223
+ var isViewingOnlyUnread = _this5.defaultOptions.status === "unread";
224
+
225
+ // If we're looking at the unread view, then we want to remove all of the items optimistically
255
226
  // from the store given that nothing should be visible. We do this by resetting the store state
256
227
  // and setting the current metadata counts to 0
257
-
258
228
  if (isViewingOnlyUnread) {
259
229
  setState(store => store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
260
230
  total_count: 0,
@@ -270,60 +240,48 @@ class Feed {
270
240
  };
271
241
  var itemIds = items.map(item => item.id);
272
242
  setState(store => store.setItemAttrs(itemIds, attrs));
273
- } // Issue the API request to the bulk status change API
274
-
243
+ }
275
244
 
245
+ // Issue the API request to the bulk status change API
276
246
  var result = yield _this5.makeBulkStatusUpdate("read");
277
-
278
247
  _this5.broadcaster.emit("items:all_read", {
279
248
  items
280
249
  });
281
-
282
250
  _this5.broadcastOverChannel("items:all_read", {
283
251
  items
284
252
  });
285
-
286
253
  return result;
287
254
  })();
288
255
  }
289
-
290
256
  markAsUnread(itemOrItems) {
291
257
  var _this6 = this;
292
-
293
258
  return _asyncToGenerator(function* () {
294
259
  _this6.optimisticallyPerformStatusUpdate(itemOrItems, "unread", {
295
260
  read_at: null
296
261
  }, "unread_count");
297
-
298
262
  return _this6.makeStatusUpdate(itemOrItems, "unread");
299
263
  })();
300
264
  }
301
-
302
265
  markAsInteracted(itemOrItems) {
303
266
  var _this7 = this;
304
-
305
267
  return _asyncToGenerator(function* () {
306
268
  var now = new Date().toISOString();
307
-
308
269
  _this7.optimisticallyPerformStatusUpdate(itemOrItems, "interacted", {
309
270
  read_at: now,
310
271
  interacted_at: now
311
272
  }, "unread_count");
312
-
313
273
  return _this7.makeStatusUpdate(itemOrItems, "interacted");
314
274
  })();
315
275
  }
276
+
316
277
  /*
317
278
  Marking one or more items as archived should:
318
279
  - Decrement the badge count for any unread / unseen items
319
280
  - Remove the item from the feed list when the `archived` flag is "exclude" (default)
320
281
  TODO: how do we handle rollbacks?
321
282
  */
322
-
323
-
324
283
  markAsArchived(itemOrItems) {
325
284
  var _this8 = this;
326
-
327
285
  return _asyncToGenerator(function* () {
328
286
  var {
329
287
  getState,
@@ -333,6 +291,7 @@ class Feed {
333
291
  var shouldOptimisticallyRemoveItems = _this8.defaultOptions.archived === "exclude";
334
292
  var normalizedItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
335
293
  var itemIds = normalizedItems.map(item => item.id);
294
+
336
295
  /*
337
296
  In the code here we want to optimistically update counts and items
338
297
  that are persisted such that we can display updates immediately on the feed
@@ -356,15 +315,16 @@ class Feed {
356
315
  // If any of the items are unseen or unread, then capture as we'll want to decrement
357
316
  // the counts for these in the metadata we have
358
317
  var unseenCount = normalizedItems.filter(i => !i.seen_at).length;
359
- var unreadCount = normalizedItems.filter(i => !i.read_at).length; // Build the new metadata
318
+ var unreadCount = normalizedItems.filter(i => !i.read_at).length;
360
319
 
320
+ // Build the new metadata
361
321
  var updatedMetadata = _objectSpread(_objectSpread({}, state.metadata), {}, {
362
322
  total_count: state.metadata.total_count - normalizedItems.length,
363
323
  unseen_count: state.metadata.unseen_count - unseenCount,
364
324
  unread_count: state.metadata.unread_count - unreadCount
365
- }); // Remove the archiving entries
366
-
325
+ });
367
326
 
327
+ // Remove the archiving entries
368
328
  var entriesToSet = state.items.filter(item => !itemIds.includes(item.id));
369
329
  setState(state => state.setResult({
370
330
  entries: entriesToSet,
@@ -377,14 +337,11 @@ class Feed {
377
337
  archived_at: new Date().toISOString()
378
338
  });
379
339
  }
380
-
381
340
  return _this8.makeStatusUpdate(itemOrItems, "archived");
382
341
  })();
383
342
  }
384
-
385
343
  markAllAsArchived() {
386
344
  var _this9 = this;
387
-
388
345
  return _asyncToGenerator(function* () {
389
346
  // Note: there is the potential for a race condition here because the bulk
390
347
  // update is an async method, so if a new message comes in during this window before
@@ -395,11 +352,11 @@ class Feed {
395
352
  } = _this9.store;
396
353
  var {
397
354
  items
398
- } = getState(); // Here if we're looking at a feed that excludes all of the archived items by default then we
399
- // will want to optimistically remove all of the items from the feed as they are now all excluded
355
+ } = getState();
400
356
 
357
+ // Here if we're looking at a feed that excludes all of the archived items by default then we
358
+ // will want to optimistically remove all of the items from the feed as they are now all excluded
401
359
  var shouldOptimisticallyRemoveItems = _this9.defaultOptions.archived === "exclude";
402
-
403
360
  if (shouldOptimisticallyRemoveItems) {
404
361
  // Reset the store to clear out all of items and reset the badge count
405
362
  setState(store => store.resetStore());
@@ -411,41 +368,33 @@ class Feed {
411
368
  archived_at: new Date().toISOString()
412
369
  });
413
370
  });
414
- } // Issue the API request to the bulk status change API
415
-
371
+ }
416
372
 
373
+ // Issue the API request to the bulk status change API
417
374
  var result = yield _this9.makeBulkStatusUpdate("archive");
418
-
419
375
  _this9.broadcaster.emit("items:all_archived", {
420
376
  items
421
377
  });
422
-
423
378
  _this9.broadcastOverChannel("items:all_archived", {
424
379
  items
425
380
  });
426
-
427
381
  return result;
428
382
  })();
429
383
  }
430
-
431
384
  markAsUnarchived(itemOrItems) {
432
385
  var _this10 = this;
433
-
434
386
  return _asyncToGenerator(function* () {
435
387
  _this10.optimisticallyPerformStatusUpdate(itemOrItems, "unarchived", {
436
388
  archived_at: null
437
389
  });
438
-
439
390
  return _this10.makeStatusUpdate(itemOrItems, "unarchived");
440
391
  })();
441
392
  }
442
- /* Fetches the feed content, appending it to the store */
443
-
444
393
 
394
+ /* Fetches the feed content, appending it to the store */
445
395
  fetch() {
446
396
  var _arguments = arguments,
447
- _this11 = this;
448
-
397
+ _this11 = this;
449
398
  return _asyncToGenerator(function* () {
450
399
  var options = _arguments.length > 0 && _arguments[0] !== undefined ? _arguments[0] : {};
451
400
  var {
@@ -454,32 +403,31 @@ class Feed {
454
403
  } = _this11.store;
455
404
  var {
456
405
  networkStatus
457
- } = getState(); // If there's an existing request in flight, then do nothing
406
+ } = getState();
458
407
 
408
+ // If there's an existing request in flight, then do nothing
459
409
  if (isRequestInFlight(networkStatus)) {
460
410
  return;
461
- } // Set the loading type based on the request type it is
462
-
411
+ }
463
412
 
413
+ // Set the loading type based on the request type it is
464
414
  setState(store => {
465
415
  var _options$__loadingTyp;
466
-
467
416
  return store.setNetworkStatus((_options$__loadingTyp = options.__loadingType) !== null && _options$__loadingTyp !== void 0 ? _options$__loadingTyp : NetworkStatus.loading);
468
- }); // Always include the default params, if they have been set
417
+ });
469
418
 
419
+ // Always include the default params, if they have been set
470
420
  var queryParams = _objectSpread(_objectSpread(_objectSpread({}, _this11.defaultOptions), options), {}, {
471
421
  // Unset options that should not be sent to the API
472
422
  __loadingType: undefined,
473
423
  __fetchSource: undefined,
474
424
  __experimentalCrossBrowserUpdates: undefined
475
425
  });
476
-
477
426
  var result = yield _this11.apiClient.makeRequest({
478
427
  method: "GET",
479
428
  url: "/v1/users/".concat(_this11.knock.userId, "/feeds/").concat(_this11.feedId),
480
429
  params: queryParams
481
430
  });
482
-
483
431
  if (result.statusCode === "error" || !result.body) {
484
432
  setState(store => store.setNetworkStatus(NetworkStatus.error));
485
433
  return {
@@ -487,13 +435,11 @@ class Feed {
487
435
  data: result.error || result.body
488
436
  };
489
437
  }
490
-
491
438
  var response = {
492
439
  entries: result.body.entries,
493
440
  meta: result.body.meta,
494
441
  page_info: result.body.page_info
495
442
  };
496
-
497
443
  if (options.before) {
498
444
  var opts = {
499
445
  shouldSetPage: false,
@@ -508,31 +454,27 @@ class Feed {
508
454
  setState(state => state.setResult(response, _opts));
509
455
  } else {
510
456
  setState(state => state.setResult(response));
511
- } // Legacy `messages.new` event, should be removed in a future version
512
-
513
-
514
- _this11.broadcast("messages.new", response); // Broadcast the appropriate event type depending on the fetch source
457
+ }
515
458
 
459
+ // Legacy `messages.new` event, should be removed in a future version
460
+ _this11.broadcast("messages.new", response);
516
461
 
462
+ // Broadcast the appropriate event type depending on the fetch source
517
463
  var feedEventType = options.__fetchSource === "socket" ? "items.received.realtime" : "items.received.page";
518
464
  var eventPayload = {
519
465
  items: response.entries,
520
466
  metadata: response.meta,
521
467
  event: feedEventType
522
468
  };
523
-
524
469
  _this11.broadcast(eventPayload.event, eventPayload);
525
-
526
470
  return {
527
471
  data: response,
528
472
  status: result.statusCode
529
473
  };
530
474
  })();
531
475
  }
532
-
533
476
  fetchNextPage() {
534
477
  var _this12 = this;
535
-
536
478
  return _asyncToGenerator(function* () {
537
479
  // Attempts to fetch the next page of results (if we have any)
538
480
  var {
@@ -541,27 +483,23 @@ class Feed {
541
483
  var {
542
484
  pageInfo
543
485
  } = getState();
544
-
545
486
  if (!pageInfo.after) {
546
487
  // Nothing more to fetch
547
488
  return;
548
489
  }
549
-
550
490
  _this12.fetch({
551
491
  after: pageInfo.after,
552
492
  __loadingType: NetworkStatus.fetchMore
553
493
  });
554
494
  })();
555
495
  }
556
-
557
496
  broadcast(eventName, data) {
558
497
  this.broadcaster.emit(eventName, data);
559
- } // Invoked when a new real-time message comes in from the socket
560
-
498
+ }
561
499
 
500
+ // Invoked when a new real-time message comes in from the socket
562
501
  onNewMessageReceived(_ref) {
563
502
  var _this13 = this;
564
-
565
503
  return _asyncToGenerator(function* () {
566
504
  var {
567
505
  metadata
@@ -574,21 +512,19 @@ class Feed {
574
512
  var {
575
513
  items
576
514
  } = getState();
577
- var currentHead = items[0]; // Optimistically set the badge counts
578
-
579
- setState(state => state.setMetadata(metadata)); // Fetch the items before the current head (if it exists)
580
-
515
+ var currentHead = items[0];
516
+ // Optimistically set the badge counts
517
+ setState(state => state.setMetadata(metadata));
518
+ // Fetch the items before the current head (if it exists)
581
519
  _this13.fetch({
582
520
  before: currentHead === null || currentHead === void 0 ? void 0 : currentHead.__cursor,
583
521
  __fetchSource: "socket"
584
522
  });
585
523
  })();
586
524
  }
587
-
588
525
  buildUserFeedId() {
589
526
  return "".concat(this.feedId, ":").concat(this.knock.userId);
590
527
  }
591
-
592
528
  optimisticallyPerformStatusUpdate(itemOrItems, type, attrs, badgeCountAttr) {
593
529
  var {
594
530
  getState,
@@ -596,47 +532,42 @@ class Feed {
596
532
  } = this.store;
597
533
  var normalizedItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
598
534
  var itemIds = normalizedItems.map(item => item.id);
599
-
600
535
  if (badgeCountAttr) {
601
536
  var {
602
537
  metadata
603
- } = getState(); // We only want to update the counts of items that have not already been counted towards the
604
- // badge count total to avoid updating the badge count unnecessarily.
538
+ } = getState();
605
539
 
540
+ // We only want to update the counts of items that have not already been counted towards the
541
+ // badge count total to avoid updating the badge count unnecessarily.
606
542
  var itemsToUpdate = normalizedItems.filter(item => {
607
543
  switch (type) {
608
544
  case "seen":
609
545
  return item.seen_at === null;
610
-
611
546
  case "unseen":
612
547
  return item.seen_at !== null;
613
-
614
548
  case "read":
615
549
  case "interacted":
616
550
  return item.read_at === null;
617
-
618
551
  case "unread":
619
552
  return item.read_at !== null;
620
-
621
553
  default:
622
554
  return true;
623
555
  }
624
- }); // Tnis is a hack to determine the direction of whether we're
625
- // adding or removing from the badge count
556
+ });
626
557
 
558
+ // Tnis is a hack to determine the direction of whether we're
559
+ // adding or removing from the badge count
627
560
  var direction = type.startsWith("un") ? itemsToUpdate.length : -itemsToUpdate.length;
628
561
  setState(store => store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, {
629
562
  [badgeCountAttr]: Math.max(0, metadata[badgeCountAttr] + direction)
630
563
  })));
631
- } // Update the items with the given attributes
632
-
564
+ }
633
565
 
566
+ // Update the items with the given attributes
634
567
  setState(store => store.setItemAttrs(itemIds, attrs));
635
568
  }
636
-
637
569
  makeStatusUpdate(itemOrItems, type) {
638
570
  var _this14 = this;
639
-
640
571
  return _asyncToGenerator(function* () {
641
572
  // Always treat items as a batch to use the corresponding batch endpoint
642
573
  var items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
@@ -647,24 +578,21 @@ class Feed {
647
578
  data: {
648
579
  message_ids: itemIds
649
580
  }
650
- }); // Emit the event that these items had their statuses changed
651
- // Note: we do this after the update to ensure that the server event actually completed
581
+ });
652
582
 
583
+ // Emit the event that these items had their statuses changed
584
+ // Note: we do this after the update to ensure that the server event actually completed
653
585
  _this14.broadcaster.emit("items:".concat(type), {
654
586
  items
655
587
  });
656
-
657
588
  _this14.broadcastOverChannel("items:".concat(type), {
658
589
  items
659
590
  });
660
-
661
591
  return result;
662
592
  })();
663
593
  }
664
-
665
594
  makeBulkStatusUpdate(type) {
666
595
  var _this15 = this;
667
-
668
596
  return _asyncToGenerator(function* () {
669
597
  // The base scope for the call should take into account all of the options currently
670
598
  // set on the feed, as well as being scoped for the current user. We do this so that
@@ -684,15 +612,14 @@ class Feed {
684
612
  });
685
613
  })();
686
614
  }
687
-
688
615
  broadcastOverChannel(type, payload) {
689
616
  // The broadcastChannel may not be available in non-browser environments
690
617
  if (!this.broadcastChannel) {
691
618
  return;
692
- } // Here we stringify our payload and try and send as JSON such that we
693
- // don't get any `An object could not be cloned` errors when trying to broadcast
694
-
619
+ }
695
620
 
621
+ // Here we stringify our payload and try and send as JSON such that we
622
+ // don't get any `An object could not be cloned` errors when trying to broadcast
696
623
  try {
697
624
  var stringifiedPayload = JSON.parse(JSON.stringify(payload));
698
625
  this.broadcastChannel.postMessage({
@@ -704,7 +631,36 @@ class Feed {
704
631
  }
705
632
  }
706
633
 
707
- }
634
+ /**
635
+ * Listen for changes to document visibility and automatically disconnect
636
+ * or reconnect the socket after a delay
637
+ */
638
+ setupAutoSocketManager(autoManageSocketConnectionDelay) {
639
+ var disconnectDelay = autoManageSocketConnectionDelay !== null && autoManageSocketConnectionDelay !== void 0 ? autoManageSocketConnectionDelay : DEFAULT_DISCONNECT_DELAY;
640
+ document.addEventListener("visibilitychange", () => {
641
+ if (document.visibilityState === "hidden") {
642
+ // When the tab is hidden, clean up the socket connection after a delay
643
+ this.disconnectTimer = setTimeout(() => {
644
+ this.apiClient.disconnectSocket();
645
+ this.disconnectTimer = null;
646
+ }, disconnectDelay);
647
+ } else if (document.visibilityState === "visible") {
648
+ var _this$apiClient$socke;
649
+ // When the tab is visible, clear the disconnect timer if active to cancel disconnecting
650
+ // This handles cases where the tab is only briefly hidden to avoid unnecessary disconnects
651
+ if (this.disconnectTimer) {
652
+ clearTimeout(this.disconnectTimer);
653
+ this.disconnectTimer = null;
654
+ }
708
655
 
656
+ // If the socket is not connected, try to reconnect
657
+ if (!((_this$apiClient$socke = this.apiClient.socket) !== null && _this$apiClient$socke !== void 0 && _this$apiClient$socke.isConnected())) {
658
+ this.apiClient.reconnectSocket();
659
+ this.fetch();
660
+ }
661
+ }
662
+ });
663
+ }
664
+ }
709
665
  export default Feed;
710
666
  //# sourceMappingURL=feed.js.map