@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,42 +1,28 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
4
  Object.defineProperty(exports, "__esModule", {
6
5
  value: true
7
6
  });
8
7
  exports["default"] = void 0;
9
-
10
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
-
12
9
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
-
14
10
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
15
-
16
11
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
17
-
18
12
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
19
-
20
13
  var _eventemitter = require("eventemitter2");
21
-
22
14
  var _store = _interopRequireDefault(require("./store"));
23
-
24
15
  var _networkStatus = require("../../networkStatus");
25
-
26
- 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; }
27
-
28
- 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) { (0, _defineProperty2["default"])(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; }
29
-
16
+ 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; }
17
+ 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) { (0, _defineProperty2["default"])(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; }
30
18
  // Default options to apply
31
19
  var feedClientDefaults = {
32
20
  archived: "exclude"
33
21
  };
34
-
22
+ var DEFAULT_DISCONNECT_DELAY = 2000;
35
23
  var Feed = /*#__PURE__*/function () {
36
- // The raw store instance, used for binding in React and other environments
37
24
  function Feed(knock, feedId, options) {
38
25
  var _this = this;
39
-
40
26
  (0, _classCallCheck2["default"])(this, Feed);
41
27
  this.knock = knock;
42
28
  this.feedId = feedId;
@@ -46,6 +32,8 @@ var Feed = /*#__PURE__*/function () {
46
32
  (0, _defineProperty2["default"])(this, "broadcaster", void 0);
47
33
  (0, _defineProperty2["default"])(this, "defaultOptions", void 0);
48
34
  (0, _defineProperty2["default"])(this, "broadcastChannel", void 0);
35
+ (0, _defineProperty2["default"])(this, "disconnectTimer", null);
36
+ // The raw store instance, used for binding in React and other environments
49
37
  (0, _defineProperty2["default"])(this, "store", void 0);
50
38
  this.apiClient = knock.client();
51
39
  this.feedId = feedId;
@@ -55,25 +43,28 @@ var Feed = /*#__PURE__*/function () {
55
43
  wildcard: true,
56
44
  delimiter: "."
57
45
  });
58
- this.defaultOptions = _objectSpread(_objectSpread({}, feedClientDefaults), options); // In server environments we might not have a socket connection
46
+ this.defaultOptions = _objectSpread(_objectSpread({}, feedClientDefaults), options);
59
47
 
48
+ // In server environments we might not have a socket connection
60
49
  if (this.apiClient.socket) {
61
50
  this.channel = this.apiClient.socket.channel("feeds:".concat(this.userFeedId), this.defaultOptions);
62
51
  this.channel.on("new-message", function (resp) {
63
52
  return _this.onNewMessageReceived(resp);
64
53
  });
65
- } // Attempt to bind to listen to other events from this feed in different tabs
66
- // Note: here we ensure `self` is available (it's not in server rendered envs)
67
-
54
+ }
68
55
 
56
+ // Attempt to bind to listen to other events from this feed in different tabs
57
+ // Note: here we ensure `self` is available (it's not in server rendered envs)
69
58
  this.broadcastChannel = typeof self !== "undefined" && "BroadcastChannel" in self ? new BroadcastChannel("knock:feed:".concat(this.userFeedId)) : null;
59
+ if (options.auto_manage_socket_connection && this.apiClient.socket) {
60
+ this.setupAutoSocketManager(options.auto_manage_socket_connection_delay);
61
+ }
70
62
  }
63
+
71
64
  /**
72
65
  * Cleans up a feed instance by destroying the store and disconnecting
73
66
  * an open socket connection.
74
67
  */
75
-
76
-
77
68
  (0, _createClass2["default"])(Feed, [{
78
69
  key: "teardown",
79
70
  value: function teardown() {
@@ -81,36 +72,33 @@ var Feed = /*#__PURE__*/function () {
81
72
  this.channel.leave();
82
73
  this.channel.off("new-message");
83
74
  }
84
-
85
75
  this.broadcaster.removeAllListeners();
86
76
  this.store.destroy();
87
-
88
77
  if (this.broadcastChannel) {
89
78
  this.broadcastChannel.close();
90
79
  }
91
80
  }
81
+
92
82
  /*
93
83
  Initializes a real-time connection to Knock, connecting the websocket for the
94
84
  current ApiClient instance if the socket is not already connected.
95
85
  */
96
-
97
86
  }, {
98
87
  key: "listenForUpdates",
99
88
  value: function listenForUpdates() {
100
89
  var _this2 = this;
101
-
102
90
  // Connect the socket only if we don't already have a connection
103
91
  if (this.apiClient.socket && !this.apiClient.socket.isConnected()) {
104
92
  this.apiClient.socket.connect();
105
- } // Only join the channel if we're not already in a joining state
106
-
93
+ }
107
94
 
95
+ // Only join the channel if we're not already in a joining state
108
96
  if (this.channel && ["closed", "errored"].includes(this.channel.state)) {
109
97
  this.channel.join();
110
- } // Opt into receiving updates from _other tabs for the same user / feed_ via the broadcast
111
- // channel (iff it's enabled and exists)
112
-
98
+ }
113
99
 
100
+ // Opt into receiving updates from _other tabs for the same user / feed_ via the broadcast
101
+ // channel (iff it's enabled and exists)
114
102
  if (this.broadcastChannel && this.defaultOptions.__experimentalCrossBrowserUpdates === true) {
115
103
  this.broadcastChannel.onmessage = function (e) {
116
104
  switch (e.data.type) {
@@ -128,15 +116,14 @@ var Feed = /*#__PURE__*/function () {
128
116
  // maybe do this optimistically without the fetch.
129
117
  return _this2.fetch();
130
118
  break;
131
-
132
119
  default:
133
120
  return null;
134
121
  }
135
122
  };
136
123
  }
137
124
  }
138
- /* Binds a handler to be invoked when event occurs */
139
125
 
126
+ /* Binds a handler to be invoked when event occurs */
140
127
  }, {
141
128
  key: "on",
142
129
  value: function on(eventName, callback) {
@@ -158,27 +145,22 @@ var Feed = /*#__PURE__*/function () {
158
145
  var _markAsSeen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(itemOrItems) {
159
146
  var now;
160
147
  return _regenerator["default"].wrap(function _callee$(_context) {
161
- while (1) {
162
- switch (_context.prev = _context.next) {
163
- case 0:
164
- now = new Date().toISOString();
165
- this.optimisticallyPerformStatusUpdate(itemOrItems, "seen", {
166
- seen_at: now
167
- }, "unseen_count");
168
- return _context.abrupt("return", this.makeStatusUpdate(itemOrItems, "seen"));
169
-
170
- case 3:
171
- case "end":
172
- return _context.stop();
173
- }
148
+ while (1) switch (_context.prev = _context.next) {
149
+ case 0:
150
+ now = new Date().toISOString();
151
+ this.optimisticallyPerformStatusUpdate(itemOrItems, "seen", {
152
+ seen_at: now
153
+ }, "unseen_count");
154
+ return _context.abrupt("return", this.makeStatusUpdate(itemOrItems, "seen"));
155
+ case 3:
156
+ case "end":
157
+ return _context.stop();
174
158
  }
175
159
  }, _callee, this);
176
160
  }));
177
-
178
161
  function markAsSeen(_x) {
179
162
  return _markAsSeen.apply(this, arguments);
180
163
  }
181
-
182
164
  return markAsSeen;
183
165
  }()
184
166
  }, {
@@ -186,79 +168,71 @@ var Feed = /*#__PURE__*/function () {
186
168
  value: function () {
187
169
  var _markAllAsSeen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2() {
188
170
  var _this$store, getState, setState, _getState, metadata, items, isViewingOnlyUnseen, attrs, itemIds, result;
189
-
190
171
  return _regenerator["default"].wrap(function _callee2$(_context2) {
191
- while (1) {
192
- switch (_context2.prev = _context2.next) {
193
- case 0:
194
- // To mark all of the messages as seen we:
195
- // 1. Optimistically update *everything* we have in the store
196
- // 2. We decrement the `unseen_count` to zero optimistically
197
- // 3. We issue the API call to the endpoint
198
- //
199
- // Note: there is the potential for a race condition here because the bulk
200
- // update is an async method, so if a new message comes in during this window before
201
- // the update has been processed we'll effectively reset the `unseen_count` to be what it was.
202
- //
203
- // Note: here we optimistically handle the case whereby the feed is scoped to show only `unseen`
204
- // items by removing everything from view.
205
- _this$store = this.store, getState = _this$store.getState, setState = _this$store.setState;
206
- _getState = getState(), metadata = _getState.metadata, items = _getState.items;
207
- isViewingOnlyUnseen = this.defaultOptions.status === "unseen"; // If we're looking at the unseen view, then we want to remove all of the items optimistically
208
- // from the store given that nothing should be visible. We do this by resetting the store state
209
- // and setting the current metadata counts to 0
210
-
211
- if (isViewingOnlyUnseen) {
212
- setState(function (store) {
213
- return store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
214
- total_count: 0,
215
- unseen_count: 0
216
- }));
217
- });
218
- } else {
219
- // Otherwise we want to update the metadata and mark all of the items in the store as seen
220
- setState(function (store) {
221
- return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, {
222
- unseen_count: 0
223
- }));
224
- });
225
- attrs = {
226
- seen_at: new Date().toISOString()
227
- };
228
- itemIds = items.map(function (item) {
229
- return item.id;
230
- });
231
- setState(function (store) {
232
- return store.setItemAttrs(itemIds, attrs);
233
- });
234
- } // Issue the API request to the bulk status change API
235
-
236
-
237
- _context2.next = 6;
238
- return this.makeBulkStatusUpdate("seen");
239
-
240
- case 6:
241
- result = _context2.sent;
242
- this.broadcaster.emit("items:all_seen", {
243
- items: items
172
+ while (1) switch (_context2.prev = _context2.next) {
173
+ case 0:
174
+ // To mark all of the messages as seen we:
175
+ // 1. Optimistically update *everything* we have in the store
176
+ // 2. We decrement the `unseen_count` to zero optimistically
177
+ // 3. We issue the API call to the endpoint
178
+ //
179
+ // Note: there is the potential for a race condition here because the bulk
180
+ // update is an async method, so if a new message comes in during this window before
181
+ // the update has been processed we'll effectively reset the `unseen_count` to be what it was.
182
+ //
183
+ // Note: here we optimistically handle the case whereby the feed is scoped to show only `unseen`
184
+ // items by removing everything from view.
185
+ _this$store = this.store, getState = _this$store.getState, setState = _this$store.setState;
186
+ _getState = getState(), metadata = _getState.metadata, items = _getState.items;
187
+ isViewingOnlyUnseen = this.defaultOptions.status === "unseen"; // If we're looking at the unseen view, then we want to remove all of the items optimistically
188
+ // from the store given that nothing should be visible. We do this by resetting the store state
189
+ // and setting the current metadata counts to 0
190
+ if (isViewingOnlyUnseen) {
191
+ setState(function (store) {
192
+ return store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
193
+ total_count: 0,
194
+ unseen_count: 0
195
+ }));
196
+ });
197
+ } else {
198
+ // Otherwise we want to update the metadata and mark all of the items in the store as seen
199
+ setState(function (store) {
200
+ return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, {
201
+ unseen_count: 0
202
+ }));
244
203
  });
245
- this.broadcastOverChannel("items:all_seen", {
246
- items: items
204
+ attrs = {
205
+ seen_at: new Date().toISOString()
206
+ };
207
+ itemIds = items.map(function (item) {
208
+ return item.id;
247
209
  });
248
- return _context2.abrupt("return", result);
249
-
250
- case 10:
251
- case "end":
252
- return _context2.stop();
253
- }
210
+ setState(function (store) {
211
+ return store.setItemAttrs(itemIds, attrs);
212
+ });
213
+ }
214
+
215
+ // Issue the API request to the bulk status change API
216
+ _context2.next = 6;
217
+ return this.makeBulkStatusUpdate("seen");
218
+ case 6:
219
+ result = _context2.sent;
220
+ this.broadcaster.emit("items:all_seen", {
221
+ items: items
222
+ });
223
+ this.broadcastOverChannel("items:all_seen", {
224
+ items: items
225
+ });
226
+ return _context2.abrupt("return", result);
227
+ case 10:
228
+ case "end":
229
+ return _context2.stop();
254
230
  }
255
231
  }, _callee2, this);
256
232
  }));
257
-
258
233
  function markAllAsSeen() {
259
234
  return _markAllAsSeen.apply(this, arguments);
260
235
  }
261
-
262
236
  return markAllAsSeen;
263
237
  }()
264
238
  }, {
@@ -266,26 +240,21 @@ var Feed = /*#__PURE__*/function () {
266
240
  value: function () {
267
241
  var _markAsUnseen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(itemOrItems) {
268
242
  return _regenerator["default"].wrap(function _callee3$(_context3) {
269
- while (1) {
270
- switch (_context3.prev = _context3.next) {
271
- case 0:
272
- this.optimisticallyPerformStatusUpdate(itemOrItems, "unseen", {
273
- seen_at: null
274
- }, "unseen_count");
275
- return _context3.abrupt("return", this.makeStatusUpdate(itemOrItems, "unseen"));
276
-
277
- case 2:
278
- case "end":
279
- return _context3.stop();
280
- }
243
+ while (1) switch (_context3.prev = _context3.next) {
244
+ case 0:
245
+ this.optimisticallyPerformStatusUpdate(itemOrItems, "unseen", {
246
+ seen_at: null
247
+ }, "unseen_count");
248
+ return _context3.abrupt("return", this.makeStatusUpdate(itemOrItems, "unseen"));
249
+ case 2:
250
+ case "end":
251
+ return _context3.stop();
281
252
  }
282
253
  }, _callee3, this);
283
254
  }));
284
-
285
255
  function markAsUnseen(_x2) {
286
256
  return _markAsUnseen.apply(this, arguments);
287
257
  }
288
-
289
258
  return markAsUnseen;
290
259
  }()
291
260
  }, {
@@ -294,27 +263,22 @@ var Feed = /*#__PURE__*/function () {
294
263
  var _markAsRead = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(itemOrItems) {
295
264
  var now;
296
265
  return _regenerator["default"].wrap(function _callee4$(_context4) {
297
- while (1) {
298
- switch (_context4.prev = _context4.next) {
299
- case 0:
300
- now = new Date().toISOString();
301
- this.optimisticallyPerformStatusUpdate(itemOrItems, "read", {
302
- read_at: now
303
- }, "unread_count");
304
- return _context4.abrupt("return", this.makeStatusUpdate(itemOrItems, "read"));
305
-
306
- case 3:
307
- case "end":
308
- return _context4.stop();
309
- }
266
+ while (1) switch (_context4.prev = _context4.next) {
267
+ case 0:
268
+ now = new Date().toISOString();
269
+ this.optimisticallyPerformStatusUpdate(itemOrItems, "read", {
270
+ read_at: now
271
+ }, "unread_count");
272
+ return _context4.abrupt("return", this.makeStatusUpdate(itemOrItems, "read"));
273
+ case 3:
274
+ case "end":
275
+ return _context4.stop();
310
276
  }
311
277
  }, _callee4, this);
312
278
  }));
313
-
314
279
  function markAsRead(_x3) {
315
280
  return _markAsRead.apply(this, arguments);
316
281
  }
317
-
318
282
  return markAsRead;
319
283
  }()
320
284
  }, {
@@ -322,79 +286,71 @@ var Feed = /*#__PURE__*/function () {
322
286
  value: function () {
323
287
  var _markAllAsRead = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5() {
324
288
  var _this$store2, getState, setState, _getState2, metadata, items, isViewingOnlyUnread, attrs, itemIds, result;
325
-
326
289
  return _regenerator["default"].wrap(function _callee5$(_context5) {
327
- while (1) {
328
- switch (_context5.prev = _context5.next) {
329
- case 0:
330
- // To mark all of the messages as read we:
331
- // 1. Optimistically update *everything* we have in the store
332
- // 2. We decrement the `unread_count` to zero optimistically
333
- // 3. We issue the API call to the endpoint
334
- //
335
- // Note: there is the potential for a race condition here because the bulk
336
- // update is an async method, so if a new message comes in during this window before
337
- // the update has been processed we'll effectively reset the `unread_count` to be what it was.
338
- //
339
- // Note: here we optimistically handle the case whereby the feed is scoped to show only `unread`
340
- // items by removing everything from view.
341
- _this$store2 = this.store, getState = _this$store2.getState, setState = _this$store2.setState;
342
- _getState2 = getState(), metadata = _getState2.metadata, items = _getState2.items;
343
- isViewingOnlyUnread = this.defaultOptions.status === "unread"; // If we're looking at the unread view, then we want to remove all of the items optimistically
344
- // from the store given that nothing should be visible. We do this by resetting the store state
345
- // and setting the current metadata counts to 0
346
-
347
- if (isViewingOnlyUnread) {
348
- setState(function (store) {
349
- return store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
350
- total_count: 0,
351
- unread_count: 0
352
- }));
353
- });
354
- } else {
355
- // Otherwise we want to update the metadata and mark all of the items in the store as seen
356
- setState(function (store) {
357
- return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, {
358
- unread_count: 0
359
- }));
360
- });
361
- attrs = {
362
- read_at: new Date().toISOString()
363
- };
364
- itemIds = items.map(function (item) {
365
- return item.id;
366
- });
367
- setState(function (store) {
368
- return store.setItemAttrs(itemIds, attrs);
369
- });
370
- } // Issue the API request to the bulk status change API
371
-
372
-
373
- _context5.next = 6;
374
- return this.makeBulkStatusUpdate("read");
375
-
376
- case 6:
377
- result = _context5.sent;
378
- this.broadcaster.emit("items:all_read", {
379
- items: items
290
+ while (1) switch (_context5.prev = _context5.next) {
291
+ case 0:
292
+ // To mark all of the messages as read we:
293
+ // 1. Optimistically update *everything* we have in the store
294
+ // 2. We decrement the `unread_count` to zero optimistically
295
+ // 3. We issue the API call to the endpoint
296
+ //
297
+ // Note: there is the potential for a race condition here because the bulk
298
+ // update is an async method, so if a new message comes in during this window before
299
+ // the update has been processed we'll effectively reset the `unread_count` to be what it was.
300
+ //
301
+ // Note: here we optimistically handle the case whereby the feed is scoped to show only `unread`
302
+ // items by removing everything from view.
303
+ _this$store2 = this.store, getState = _this$store2.getState, setState = _this$store2.setState;
304
+ _getState2 = getState(), metadata = _getState2.metadata, items = _getState2.items;
305
+ isViewingOnlyUnread = this.defaultOptions.status === "unread"; // If we're looking at the unread view, then we want to remove all of the items optimistically
306
+ // from the store given that nothing should be visible. We do this by resetting the store state
307
+ // and setting the current metadata counts to 0
308
+ if (isViewingOnlyUnread) {
309
+ setState(function (store) {
310
+ return store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
311
+ total_count: 0,
312
+ unread_count: 0
313
+ }));
380
314
  });
381
- this.broadcastOverChannel("items:all_read", {
382
- items: items
315
+ } else {
316
+ // Otherwise we want to update the metadata and mark all of the items in the store as seen
317
+ setState(function (store) {
318
+ return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, {
319
+ unread_count: 0
320
+ }));
383
321
  });
384
- return _context5.abrupt("return", result);
385
-
386
- case 10:
387
- case "end":
388
- return _context5.stop();
389
- }
322
+ attrs = {
323
+ read_at: new Date().toISOString()
324
+ };
325
+ itemIds = items.map(function (item) {
326
+ return item.id;
327
+ });
328
+ setState(function (store) {
329
+ return store.setItemAttrs(itemIds, attrs);
330
+ });
331
+ }
332
+
333
+ // Issue the API request to the bulk status change API
334
+ _context5.next = 6;
335
+ return this.makeBulkStatusUpdate("read");
336
+ case 6:
337
+ result = _context5.sent;
338
+ this.broadcaster.emit("items:all_read", {
339
+ items: items
340
+ });
341
+ this.broadcastOverChannel("items:all_read", {
342
+ items: items
343
+ });
344
+ return _context5.abrupt("return", result);
345
+ case 10:
346
+ case "end":
347
+ return _context5.stop();
390
348
  }
391
349
  }, _callee5, this);
392
350
  }));
393
-
394
351
  function markAllAsRead() {
395
352
  return _markAllAsRead.apply(this, arguments);
396
353
  }
397
-
398
354
  return markAllAsRead;
399
355
  }()
400
356
  }, {
@@ -402,26 +358,21 @@ var Feed = /*#__PURE__*/function () {
402
358
  value: function () {
403
359
  var _markAsUnread = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6(itemOrItems) {
404
360
  return _regenerator["default"].wrap(function _callee6$(_context6) {
405
- while (1) {
406
- switch (_context6.prev = _context6.next) {
407
- case 0:
408
- this.optimisticallyPerformStatusUpdate(itemOrItems, "unread", {
409
- read_at: null
410
- }, "unread_count");
411
- return _context6.abrupt("return", this.makeStatusUpdate(itemOrItems, "unread"));
412
-
413
- case 2:
414
- case "end":
415
- return _context6.stop();
416
- }
361
+ while (1) switch (_context6.prev = _context6.next) {
362
+ case 0:
363
+ this.optimisticallyPerformStatusUpdate(itemOrItems, "unread", {
364
+ read_at: null
365
+ }, "unread_count");
366
+ return _context6.abrupt("return", this.makeStatusUpdate(itemOrItems, "unread"));
367
+ case 2:
368
+ case "end":
369
+ return _context6.stop();
417
370
  }
418
371
  }, _callee6, this);
419
372
  }));
420
-
421
373
  function markAsUnread(_x4) {
422
374
  return _markAsUnread.apply(this, arguments);
423
375
  }
424
-
425
376
  return markAsUnread;
426
377
  }()
427
378
  }, {
@@ -430,28 +381,23 @@ var Feed = /*#__PURE__*/function () {
430
381
  var _markAsInteracted = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee7(itemOrItems) {
431
382
  var now;
432
383
  return _regenerator["default"].wrap(function _callee7$(_context7) {
433
- while (1) {
434
- switch (_context7.prev = _context7.next) {
435
- case 0:
436
- now = new Date().toISOString();
437
- this.optimisticallyPerformStatusUpdate(itemOrItems, "interacted", {
438
- read_at: now,
439
- interacted_at: now
440
- }, "unread_count");
441
- return _context7.abrupt("return", this.makeStatusUpdate(itemOrItems, "interacted"));
442
-
443
- case 3:
444
- case "end":
445
- return _context7.stop();
446
- }
384
+ while (1) switch (_context7.prev = _context7.next) {
385
+ case 0:
386
+ now = new Date().toISOString();
387
+ this.optimisticallyPerformStatusUpdate(itemOrItems, "interacted", {
388
+ read_at: now,
389
+ interacted_at: now
390
+ }, "unread_count");
391
+ return _context7.abrupt("return", this.makeStatusUpdate(itemOrItems, "interacted"));
392
+ case 3:
393
+ case "end":
394
+ return _context7.stop();
447
395
  }
448
396
  }, _callee7, this);
449
397
  }));
450
-
451
398
  function markAsInteracted(_x5) {
452
399
  return _markAsInteracted.apply(this, arguments);
453
400
  }
454
-
455
401
  return markAsInteracted;
456
402
  }()
457
403
  /*
@@ -460,154 +406,134 @@ var Feed = /*#__PURE__*/function () {
460
406
  - Remove the item from the feed list when the `archived` flag is "exclude" (default)
461
407
  TODO: how do we handle rollbacks?
462
408
  */
463
-
464
409
  }, {
465
410
  key: "markAsArchived",
466
- value: function () {
411
+ value: (function () {
467
412
  var _markAsArchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee8(itemOrItems) {
468
413
  var _this$store3, getState, setState, state, shouldOptimisticallyRemoveItems, normalizedItems, itemIds, unseenCount, unreadCount, updatedMetadata, entriesToSet;
469
-
470
414
  return _regenerator["default"].wrap(function _callee8$(_context8) {
471
- while (1) {
472
- switch (_context8.prev = _context8.next) {
473
- case 0:
474
- _this$store3 = this.store, getState = _this$store3.getState, setState = _this$store3.setState;
475
- state = getState();
476
- shouldOptimisticallyRemoveItems = this.defaultOptions.archived === "exclude";
477
- normalizedItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
478
- itemIds = normalizedItems.map(function (item) {
479
- return item.id;
415
+ while (1) switch (_context8.prev = _context8.next) {
416
+ case 0:
417
+ _this$store3 = this.store, getState = _this$store3.getState, setState = _this$store3.setState;
418
+ state = getState();
419
+ shouldOptimisticallyRemoveItems = this.defaultOptions.archived === "exclude";
420
+ normalizedItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
421
+ itemIds = normalizedItems.map(function (item) {
422
+ return item.id;
423
+ });
424
+ /*
425
+ In the code here we want to optimistically update counts and items
426
+ that are persisted such that we can display updates immediately on the feed
427
+ without needing to make a network request.
428
+ Note: right now this does *not* take into account offline handling or any extensive retry
429
+ logic, so rollbacks aren't considered. That probably needs to be a future consideration for
430
+ this library.
431
+ Scenarios to consider:
432
+ ## Feed scope to archived *only*
433
+ - Counts should not be decremented
434
+ - Items should not be removed
435
+ ## Feed scoped to exclude archived items (the default)
436
+ - Counts should be decremented
437
+ - Items should be removed
438
+ ## Feed scoped to include archived items as well
439
+ - Counts should not be decremented
440
+ - Items should not be removed
441
+ */
442
+ if (shouldOptimisticallyRemoveItems) {
443
+ // If any of the items are unseen or unread, then capture as we'll want to decrement
444
+ // the counts for these in the metadata we have
445
+ unseenCount = normalizedItems.filter(function (i) {
446
+ return !i.seen_at;
447
+ }).length;
448
+ unreadCount = normalizedItems.filter(function (i) {
449
+ return !i.read_at;
450
+ }).length; // Build the new metadata
451
+ updatedMetadata = _objectSpread(_objectSpread({}, state.metadata), {}, {
452
+ total_count: state.metadata.total_count - normalizedItems.length,
453
+ unseen_count: state.metadata.unseen_count - unseenCount,
454
+ unread_count: state.metadata.unread_count - unreadCount
455
+ }); // Remove the archiving entries
456
+ entriesToSet = state.items.filter(function (item) {
457
+ return !itemIds.includes(item.id);
480
458
  });
481
- /*
482
- In the code here we want to optimistically update counts and items
483
- that are persisted such that we can display updates immediately on the feed
484
- without needing to make a network request.
485
- Note: right now this does *not* take into account offline handling or any extensive retry
486
- logic, so rollbacks aren't considered. That probably needs to be a future consideration for
487
- this library.
488
- Scenarios to consider:
489
- ## Feed scope to archived *only*
490
- - Counts should not be decremented
491
- - Items should not be removed
492
- ## Feed scoped to exclude archived items (the default)
493
- - Counts should be decremented
494
- - Items should be removed
495
- ## Feed scoped to include archived items as well
496
- - Counts should not be decremented
497
- - Items should not be removed
498
- */
499
-
500
- if (shouldOptimisticallyRemoveItems) {
501
- // If any of the items are unseen or unread, then capture as we'll want to decrement
502
- // the counts for these in the metadata we have
503
- unseenCount = normalizedItems.filter(function (i) {
504
- return !i.seen_at;
505
- }).length;
506
- unreadCount = normalizedItems.filter(function (i) {
507
- return !i.read_at;
508
- }).length; // Build the new metadata
509
-
510
- updatedMetadata = _objectSpread(_objectSpread({}, state.metadata), {}, {
511
- total_count: state.metadata.total_count - normalizedItems.length,
512
- unseen_count: state.metadata.unseen_count - unseenCount,
513
- unread_count: state.metadata.unread_count - unreadCount
514
- }); // Remove the archiving entries
515
-
516
- entriesToSet = state.items.filter(function (item) {
517
- return !itemIds.includes(item.id);
518
- });
519
- setState(function (state) {
520
- return state.setResult({
521
- entries: entriesToSet,
522
- meta: updatedMetadata,
523
- page_info: state.pageInfo
524
- });
525
- });
526
- } else {
527
- // Mark all the entries being updated as archived either way so the state is correct
528
- state.setItemAttrs(itemIds, {
529
- archived_at: new Date().toISOString()
459
+ setState(function (state) {
460
+ return state.setResult({
461
+ entries: entriesToSet,
462
+ meta: updatedMetadata,
463
+ page_info: state.pageInfo
530
464
  });
531
- }
532
-
533
- return _context8.abrupt("return", this.makeStatusUpdate(itemOrItems, "archived"));
534
-
535
- case 7:
536
- case "end":
537
- return _context8.stop();
538
- }
465
+ });
466
+ } else {
467
+ // Mark all the entries being updated as archived either way so the state is correct
468
+ state.setItemAttrs(itemIds, {
469
+ archived_at: new Date().toISOString()
470
+ });
471
+ }
472
+ return _context8.abrupt("return", this.makeStatusUpdate(itemOrItems, "archived"));
473
+ case 7:
474
+ case "end":
475
+ return _context8.stop();
539
476
  }
540
477
  }, _callee8, this);
541
478
  }));
542
-
543
479
  function markAsArchived(_x6) {
544
480
  return _markAsArchived.apply(this, arguments);
545
481
  }
546
-
547
482
  return markAsArchived;
548
- }()
483
+ }())
549
484
  }, {
550
485
  key: "markAllAsArchived",
551
486
  value: function () {
552
487
  var _markAllAsArchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee9() {
553
488
  var _this$store4, setState, getState, _getState3, items, shouldOptimisticallyRemoveItems, result;
554
-
555
489
  return _regenerator["default"].wrap(function _callee9$(_context9) {
556
- while (1) {
557
- switch (_context9.prev = _context9.next) {
558
- case 0:
559
- // Note: there is the potential for a race condition here because the bulk
560
- // update is an async method, so if a new message comes in during this window before
561
- // the update has been processed we'll effectively reset the `unseen_count` to be what it was.
562
- _this$store4 = this.store, setState = _this$store4.setState, getState = _this$store4.getState;
563
- _getState3 = getState(), items = _getState3.items; // Here if we're looking at a feed that excludes all of the archived items by default then we
564
- // will want to optimistically remove all of the items from the feed as they are now all excluded
565
-
566
- shouldOptimisticallyRemoveItems = this.defaultOptions.archived === "exclude";
567
-
568
- if (shouldOptimisticallyRemoveItems) {
569
- // Reset the store to clear out all of items and reset the badge count
570
- setState(function (store) {
571
- return store.resetStore();
490
+ while (1) switch (_context9.prev = _context9.next) {
491
+ case 0:
492
+ // Note: there is the potential for a race condition here because the bulk
493
+ // update is an async method, so if a new message comes in during this window before
494
+ // the update has been processed we'll effectively reset the `unseen_count` to be what it was.
495
+ _this$store4 = this.store, setState = _this$store4.setState, getState = _this$store4.getState;
496
+ _getState3 = getState(), items = _getState3.items; // Here if we're looking at a feed that excludes all of the archived items by default then we
497
+ // will want to optimistically remove all of the items from the feed as they are now all excluded
498
+ shouldOptimisticallyRemoveItems = this.defaultOptions.archived === "exclude";
499
+ if (shouldOptimisticallyRemoveItems) {
500
+ // Reset the store to clear out all of items and reset the badge count
501
+ setState(function (store) {
502
+ return store.resetStore();
503
+ });
504
+ } else {
505
+ // Mark all the entries being updated as archived either way so the state is correct
506
+ setState(function (store) {
507
+ var itemIds = items.map(function (i) {
508
+ return i.id;
572
509
  });
573
- } else {
574
- // Mark all the entries being updated as archived either way so the state is correct
575
- setState(function (store) {
576
- var itemIds = items.map(function (i) {
577
- return i.id;
578
- });
579
- store.setItemAttrs(itemIds, {
580
- archived_at: new Date().toISOString()
581
- });
510
+ store.setItemAttrs(itemIds, {
511
+ archived_at: new Date().toISOString()
582
512
  });
583
- } // Issue the API request to the bulk status change API
584
-
585
-
586
- _context9.next = 6;
587
- return this.makeBulkStatusUpdate("archive");
588
-
589
- case 6:
590
- result = _context9.sent;
591
- this.broadcaster.emit("items:all_archived", {
592
- items: items
593
513
  });
594
- this.broadcastOverChannel("items:all_archived", {
595
- items: items
596
- });
597
- return _context9.abrupt("return", result);
598
-
599
- case 10:
600
- case "end":
601
- return _context9.stop();
602
- }
514
+ }
515
+
516
+ // Issue the API request to the bulk status change API
517
+ _context9.next = 6;
518
+ return this.makeBulkStatusUpdate("archive");
519
+ case 6:
520
+ result = _context9.sent;
521
+ this.broadcaster.emit("items:all_archived", {
522
+ items: items
523
+ });
524
+ this.broadcastOverChannel("items:all_archived", {
525
+ items: items
526
+ });
527
+ return _context9.abrupt("return", result);
528
+ case 10:
529
+ case "end":
530
+ return _context9.stop();
603
531
  }
604
532
  }, _callee9, this);
605
533
  }));
606
-
607
534
  function markAllAsArchived() {
608
535
  return _markAllAsArchived.apply(this, arguments);
609
536
  }
610
-
611
537
  return markAllAsArchived;
612
538
  }()
613
539
  }, {
@@ -615,243 +541,208 @@ var Feed = /*#__PURE__*/function () {
615
541
  value: function () {
616
542
  var _markAsUnarchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee10(itemOrItems) {
617
543
  return _regenerator["default"].wrap(function _callee10$(_context10) {
618
- while (1) {
619
- switch (_context10.prev = _context10.next) {
620
- case 0:
621
- this.optimisticallyPerformStatusUpdate(itemOrItems, "unarchived", {
622
- archived_at: null
623
- });
624
- return _context10.abrupt("return", this.makeStatusUpdate(itemOrItems, "unarchived"));
625
-
626
- case 2:
627
- case "end":
628
- return _context10.stop();
629
- }
544
+ while (1) switch (_context10.prev = _context10.next) {
545
+ case 0:
546
+ this.optimisticallyPerformStatusUpdate(itemOrItems, "unarchived", {
547
+ archived_at: null
548
+ });
549
+ return _context10.abrupt("return", this.makeStatusUpdate(itemOrItems, "unarchived"));
550
+ case 2:
551
+ case "end":
552
+ return _context10.stop();
630
553
  }
631
554
  }, _callee10, this);
632
555
  }));
633
-
634
556
  function markAsUnarchived(_x7) {
635
557
  return _markAsUnarchived.apply(this, arguments);
636
558
  }
637
-
638
559
  return markAsUnarchived;
639
- }()
640
- /* Fetches the feed content, appending it to the store */
641
-
560
+ }() /* Fetches the feed content, appending it to the store */
642
561
  }, {
643
562
  key: "fetch",
644
- value: function () {
563
+ value: (function () {
645
564
  var _fetch = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee11() {
646
565
  var options,
647
- _this$store5,
648
- setState,
649
- getState,
650
- _getState4,
651
- networkStatus,
652
- queryParams,
653
- result,
654
- response,
655
- opts,
656
- _opts,
657
- feedEventType,
658
- eventPayload,
659
- _args11 = arguments;
660
-
566
+ _this$store5,
567
+ setState,
568
+ getState,
569
+ _getState4,
570
+ networkStatus,
571
+ queryParams,
572
+ result,
573
+ response,
574
+ opts,
575
+ _opts,
576
+ feedEventType,
577
+ eventPayload,
578
+ _args11 = arguments;
661
579
  return _regenerator["default"].wrap(function _callee11$(_context11) {
662
- while (1) {
663
- switch (_context11.prev = _context11.next) {
664
- case 0:
665
- options = _args11.length > 0 && _args11[0] !== undefined ? _args11[0] : {};
666
- _this$store5 = this.store, setState = _this$store5.setState, getState = _this$store5.getState;
667
- _getState4 = getState(), networkStatus = _getState4.networkStatus; // If there's an existing request in flight, then do nothing
668
-
669
- if (!(0, _networkStatus.isRequestInFlight)(networkStatus)) {
670
- _context11.next = 5;
671
- break;
672
- }
673
-
674
- return _context11.abrupt("return");
675
-
676
- case 5:
677
- // Set the loading type based on the request type it is
678
- setState(function (store) {
679
- var _options$__loadingTyp;
680
-
681
- return store.setNetworkStatus((_options$__loadingTyp = options.__loadingType) !== null && _options$__loadingTyp !== void 0 ? _options$__loadingTyp : _networkStatus.NetworkStatus.loading);
682
- }); // Always include the default params, if they have been set
683
-
684
- queryParams = _objectSpread(_objectSpread(_objectSpread({}, this.defaultOptions), options), {}, {
685
- // Unset options that should not be sent to the API
686
- __loadingType: undefined,
687
- __fetchSource: undefined,
688
- __experimentalCrossBrowserUpdates: undefined
689
- });
690
- _context11.next = 9;
691
- return this.apiClient.makeRequest({
692
- method: "GET",
693
- url: "/v1/users/".concat(this.knock.userId, "/feeds/").concat(this.feedId),
694
- params: queryParams
695
- });
696
-
697
- case 9:
698
- result = _context11.sent;
699
-
700
- if (!(result.statusCode === "error" || !result.body)) {
701
- _context11.next = 13;
702
- break;
703
- }
704
-
705
- setState(function (store) {
706
- return store.setNetworkStatus(_networkStatus.NetworkStatus.error);
707
- });
708
- return _context11.abrupt("return", {
709
- status: result.statusCode,
710
- data: result.error || result.body
711
- });
712
-
713
- case 13:
714
- response = {
715
- entries: result.body.entries,
716
- meta: result.body.meta,
717
- page_info: result.body.page_info
580
+ while (1) switch (_context11.prev = _context11.next) {
581
+ case 0:
582
+ options = _args11.length > 0 && _args11[0] !== undefined ? _args11[0] : {};
583
+ _this$store5 = this.store, setState = _this$store5.setState, getState = _this$store5.getState;
584
+ _getState4 = getState(), networkStatus = _getState4.networkStatus; // If there's an existing request in flight, then do nothing
585
+ if (!(0, _networkStatus.isRequestInFlight)(networkStatus)) {
586
+ _context11.next = 5;
587
+ break;
588
+ }
589
+ return _context11.abrupt("return");
590
+ case 5:
591
+ // Set the loading type based on the request type it is
592
+ setState(function (store) {
593
+ var _options$__loadingTyp;
594
+ return store.setNetworkStatus((_options$__loadingTyp = options.__loadingType) !== null && _options$__loadingTyp !== void 0 ? _options$__loadingTyp : _networkStatus.NetworkStatus.loading);
595
+ });
596
+
597
+ // Always include the default params, if they have been set
598
+ queryParams = _objectSpread(_objectSpread(_objectSpread({}, this.defaultOptions), options), {}, {
599
+ // Unset options that should not be sent to the API
600
+ __loadingType: undefined,
601
+ __fetchSource: undefined,
602
+ __experimentalCrossBrowserUpdates: undefined
603
+ });
604
+ _context11.next = 9;
605
+ return this.apiClient.makeRequest({
606
+ method: "GET",
607
+ url: "/v1/users/".concat(this.knock.userId, "/feeds/").concat(this.feedId),
608
+ params: queryParams
609
+ });
610
+ case 9:
611
+ result = _context11.sent;
612
+ if (!(result.statusCode === "error" || !result.body)) {
613
+ _context11.next = 13;
614
+ break;
615
+ }
616
+ setState(function (store) {
617
+ return store.setNetworkStatus(_networkStatus.NetworkStatus.error);
618
+ });
619
+ return _context11.abrupt("return", {
620
+ status: result.statusCode,
621
+ data: result.error || result.body
622
+ });
623
+ case 13:
624
+ response = {
625
+ entries: result.body.entries,
626
+ meta: result.body.meta,
627
+ page_info: result.body.page_info
628
+ };
629
+ if (options.before) {
630
+ opts = {
631
+ shouldSetPage: false,
632
+ shouldAppend: true
718
633
  };
719
-
720
- if (options.before) {
721
- opts = {
722
- shouldSetPage: false,
723
- shouldAppend: true
724
- };
725
- setState(function (state) {
726
- return state.setResult(response, opts);
727
- });
728
- } else if (options.after) {
729
- _opts = {
730
- shouldSetPage: true,
731
- shouldAppend: true
732
- };
733
- setState(function (state) {
734
- return state.setResult(response, _opts);
735
- });
736
- } else {
737
- setState(function (state) {
738
- return state.setResult(response);
739
- });
740
- } // Legacy `messages.new` event, should be removed in a future version
741
-
742
-
743
- this.broadcast("messages.new", response); // Broadcast the appropriate event type depending on the fetch source
744
-
745
- feedEventType = options.__fetchSource === "socket" ? "items.received.realtime" : "items.received.page";
746
- eventPayload = {
747
- items: response.entries,
748
- metadata: response.meta,
749
- event: feedEventType
634
+ setState(function (state) {
635
+ return state.setResult(response, opts);
636
+ });
637
+ } else if (options.after) {
638
+ _opts = {
639
+ shouldSetPage: true,
640
+ shouldAppend: true
750
641
  };
751
- this.broadcast(eventPayload.event, eventPayload);
752
- return _context11.abrupt("return", {
753
- data: response,
754
- status: result.statusCode
642
+ setState(function (state) {
643
+ return state.setResult(response, _opts);
755
644
  });
756
-
757
- case 20:
758
- case "end":
759
- return _context11.stop();
760
- }
645
+ } else {
646
+ setState(function (state) {
647
+ return state.setResult(response);
648
+ });
649
+ }
650
+
651
+ // Legacy `messages.new` event, should be removed in a future version
652
+ this.broadcast("messages.new", response);
653
+
654
+ // Broadcast the appropriate event type depending on the fetch source
655
+ feedEventType = options.__fetchSource === "socket" ? "items.received.realtime" : "items.received.page";
656
+ eventPayload = {
657
+ items: response.entries,
658
+ metadata: response.meta,
659
+ event: feedEventType
660
+ };
661
+ this.broadcast(eventPayload.event, eventPayload);
662
+ return _context11.abrupt("return", {
663
+ data: response,
664
+ status: result.statusCode
665
+ });
666
+ case 20:
667
+ case "end":
668
+ return _context11.stop();
761
669
  }
762
670
  }, _callee11, this);
763
671
  }));
764
-
765
672
  function fetch() {
766
673
  return _fetch.apply(this, arguments);
767
674
  }
768
-
769
675
  return fetch;
770
- }()
676
+ }())
771
677
  }, {
772
678
  key: "fetchNextPage",
773
679
  value: function () {
774
680
  var _fetchNextPage = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee12() {
775
681
  var getState, _getState5, pageInfo;
776
-
777
682
  return _regenerator["default"].wrap(function _callee12$(_context12) {
778
- while (1) {
779
- switch (_context12.prev = _context12.next) {
780
- case 0:
781
- // Attempts to fetch the next page of results (if we have any)
782
- getState = this.store.getState;
783
- _getState5 = getState(), pageInfo = _getState5.pageInfo;
784
-
785
- if (pageInfo.after) {
786
- _context12.next = 4;
787
- break;
788
- }
789
-
790
- return _context12.abrupt("return");
791
-
792
- case 4:
793
- this.fetch({
794
- after: pageInfo.after,
795
- __loadingType: _networkStatus.NetworkStatus.fetchMore
796
- });
797
-
798
- case 5:
799
- case "end":
800
- return _context12.stop();
801
- }
683
+ while (1) switch (_context12.prev = _context12.next) {
684
+ case 0:
685
+ // Attempts to fetch the next page of results (if we have any)
686
+ getState = this.store.getState;
687
+ _getState5 = getState(), pageInfo = _getState5.pageInfo;
688
+ if (pageInfo.after) {
689
+ _context12.next = 4;
690
+ break;
691
+ }
692
+ return _context12.abrupt("return");
693
+ case 4:
694
+ this.fetch({
695
+ after: pageInfo.after,
696
+ __loadingType: _networkStatus.NetworkStatus.fetchMore
697
+ });
698
+ case 5:
699
+ case "end":
700
+ return _context12.stop();
802
701
  }
803
702
  }, _callee12, this);
804
703
  }));
805
-
806
704
  function fetchNextPage() {
807
705
  return _fetchNextPage.apply(this, arguments);
808
706
  }
809
-
810
707
  return fetchNextPage;
811
708
  }()
812
709
  }, {
813
710
  key: "broadcast",
814
711
  value: function broadcast(eventName, data) {
815
712
  this.broadcaster.emit(eventName, data);
816
- } // Invoked when a new real-time message comes in from the socket
713
+ }
817
714
 
715
+ // Invoked when a new real-time message comes in from the socket
818
716
  }, {
819
717
  key: "onNewMessageReceived",
820
718
  value: function () {
821
719
  var _onNewMessageReceived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee13(_ref) {
822
720
  var metadata, _this$store6, getState, setState, _getState6, items, currentHead;
823
-
824
721
  return _regenerator["default"].wrap(function _callee13$(_context13) {
825
- while (1) {
826
- switch (_context13.prev = _context13.next) {
827
- case 0:
828
- metadata = _ref.metadata;
829
- // Handle the new message coming in
830
- _this$store6 = this.store, getState = _this$store6.getState, setState = _this$store6.setState;
831
- _getState6 = getState(), items = _getState6.items;
832
- currentHead = items[0]; // Optimistically set the badge counts
833
-
834
- setState(function (state) {
835
- return state.setMetadata(metadata);
836
- }); // Fetch the items before the current head (if it exists)
837
-
838
- this.fetch({
839
- before: currentHead === null || currentHead === void 0 ? void 0 : currentHead.__cursor,
840
- __fetchSource: "socket"
841
- });
842
-
843
- case 6:
844
- case "end":
845
- return _context13.stop();
846
- }
722
+ while (1) switch (_context13.prev = _context13.next) {
723
+ case 0:
724
+ metadata = _ref.metadata;
725
+ // Handle the new message coming in
726
+ _this$store6 = this.store, getState = _this$store6.getState, setState = _this$store6.setState;
727
+ _getState6 = getState(), items = _getState6.items;
728
+ currentHead = items[0]; // Optimistically set the badge counts
729
+ setState(function (state) {
730
+ return state.setMetadata(metadata);
731
+ });
732
+ // Fetch the items before the current head (if it exists)
733
+ this.fetch({
734
+ before: currentHead === null || currentHead === void 0 ? void 0 : currentHead.__cursor,
735
+ __fetchSource: "socket"
736
+ });
737
+ case 6:
738
+ case "end":
739
+ return _context13.stop();
847
740
  }
848
741
  }, _callee13, this);
849
742
  }));
850
-
851
743
  function onNewMessageReceived(_x8) {
852
744
  return _onNewMessageReceived.apply(this, arguments);
853
745
  }
854
-
855
746
  return onNewMessageReceived;
856
747
  }()
857
748
  }, {
@@ -863,47 +754,43 @@ var Feed = /*#__PURE__*/function () {
863
754
  key: "optimisticallyPerformStatusUpdate",
864
755
  value: function optimisticallyPerformStatusUpdate(itemOrItems, type, attrs, badgeCountAttr) {
865
756
  var _this$store7 = this.store,
866
- getState = _this$store7.getState,
867
- setState = _this$store7.setState;
757
+ getState = _this$store7.getState,
758
+ setState = _this$store7.setState;
868
759
  var normalizedItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
869
760
  var itemIds = normalizedItems.map(function (item) {
870
761
  return item.id;
871
762
  });
872
-
873
763
  if (badgeCountAttr) {
874
764
  var _getState7 = getState(),
875
- metadata = _getState7.metadata; // We only want to update the counts of items that have not already been counted towards the
876
- // badge count total to avoid updating the badge count unnecessarily.
877
-
765
+ metadata = _getState7.metadata;
878
766
 
767
+ // We only want to update the counts of items that have not already been counted towards the
768
+ // badge count total to avoid updating the badge count unnecessarily.
879
769
  var itemsToUpdate = normalizedItems.filter(function (item) {
880
770
  switch (type) {
881
771
  case "seen":
882
772
  return item.seen_at === null;
883
-
884
773
  case "unseen":
885
774
  return item.seen_at !== null;
886
-
887
775
  case "read":
888
776
  case "interacted":
889
777
  return item.read_at === null;
890
-
891
778
  case "unread":
892
779
  return item.read_at !== null;
893
-
894
780
  default:
895
781
  return true;
896
782
  }
897
- }); // Tnis is a hack to determine the direction of whether we're
898
- // adding or removing from the badge count
783
+ });
899
784
 
785
+ // Tnis is a hack to determine the direction of whether we're
786
+ // adding or removing from the badge count
900
787
  var direction = type.startsWith("un") ? itemsToUpdate.length : -itemsToUpdate.length;
901
788
  setState(function (store) {
902
789
  return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, (0, _defineProperty2["default"])({}, badgeCountAttr, Math.max(0, metadata[badgeCountAttr] + direction))));
903
790
  });
904
- } // Update the items with the given attributes
905
-
791
+ }
906
792
 
793
+ // Update the items with the given attributes
907
794
  setState(function (store) {
908
795
  return store.setItemAttrs(itemIds, attrs);
909
796
  });
@@ -914,47 +801,41 @@ var Feed = /*#__PURE__*/function () {
914
801
  var _makeStatusUpdate = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee14(itemOrItems, type) {
915
802
  var items, itemIds, result;
916
803
  return _regenerator["default"].wrap(function _callee14$(_context14) {
917
- while (1) {
918
- switch (_context14.prev = _context14.next) {
919
- case 0:
920
- // Always treat items as a batch to use the corresponding batch endpoint
921
- items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
922
- itemIds = items.map(function (item) {
923
- return item.id;
924
- });
925
- _context14.next = 4;
926
- return this.apiClient.makeRequest({
927
- method: "POST",
928
- url: "/v1/messages/batch/".concat(type),
929
- data: {
930
- message_ids: itemIds
931
- }
932
- });
933
-
934
- case 4:
935
- result = _context14.sent;
936
- // Emit the event that these items had their statuses changed
937
- // Note: we do this after the update to ensure that the server event actually completed
938
- this.broadcaster.emit("items:".concat(type), {
939
- items: items
940
- });
941
- this.broadcastOverChannel("items:".concat(type), {
942
- items: items
943
- });
944
- return _context14.abrupt("return", result);
945
-
946
- case 8:
947
- case "end":
948
- return _context14.stop();
949
- }
804
+ while (1) switch (_context14.prev = _context14.next) {
805
+ case 0:
806
+ // Always treat items as a batch to use the corresponding batch endpoint
807
+ items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
808
+ itemIds = items.map(function (item) {
809
+ return item.id;
810
+ });
811
+ _context14.next = 4;
812
+ return this.apiClient.makeRequest({
813
+ method: "POST",
814
+ url: "/v1/messages/batch/".concat(type),
815
+ data: {
816
+ message_ids: itemIds
817
+ }
818
+ });
819
+ case 4:
820
+ result = _context14.sent;
821
+ // Emit the event that these items had their statuses changed
822
+ // Note: we do this after the update to ensure that the server event actually completed
823
+ this.broadcaster.emit("items:".concat(type), {
824
+ items: items
825
+ });
826
+ this.broadcastOverChannel("items:".concat(type), {
827
+ items: items
828
+ });
829
+ return _context14.abrupt("return", result);
830
+ case 8:
831
+ case "end":
832
+ return _context14.stop();
950
833
  }
951
834
  }, _callee14, this);
952
835
  }));
953
-
954
836
  function makeStatusUpdate(_x9, _x10) {
955
837
  return _makeStatusUpdate.apply(this, arguments);
956
838
  }
957
-
958
839
  return makeStatusUpdate;
959
840
  }()
960
841
  }, {
@@ -963,42 +844,36 @@ var Feed = /*#__PURE__*/function () {
963
844
  var _makeBulkStatusUpdate = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee15(type) {
964
845
  var options;
965
846
  return _regenerator["default"].wrap(function _callee15$(_context15) {
966
- while (1) {
967
- switch (_context15.prev = _context15.next) {
968
- case 0:
969
- // The base scope for the call should take into account all of the options currently
970
- // set on the feed, as well as being scoped for the current user. We do this so that
971
- // we ONLY make changes to the messages that are currently in view on this feed, and not
972
- // all messages that exist.
973
- options = {
974
- user_ids: [this.knock.userId],
975
- engagement_status: this.defaultOptions.status !== "all" ? this.defaultOptions.status : undefined,
976
- archived: this.defaultOptions.archived,
977
- has_tenant: this.defaultOptions.has_tenant,
978
- tenants: this.defaultOptions.tenant ? [this.defaultOptions.tenant] : undefined
979
- };
980
- _context15.next = 3;
981
- return this.apiClient.makeRequest({
982
- method: "POST",
983
- url: "/v1/channels/".concat(this.feedId, "/messages/bulk/").concat(type),
984
- data: options
985
- });
986
-
987
- case 3:
988
- return _context15.abrupt("return", _context15.sent);
989
-
990
- case 4:
991
- case "end":
992
- return _context15.stop();
993
- }
847
+ while (1) switch (_context15.prev = _context15.next) {
848
+ case 0:
849
+ // The base scope for the call should take into account all of the options currently
850
+ // set on the feed, as well as being scoped for the current user. We do this so that
851
+ // we ONLY make changes to the messages that are currently in view on this feed, and not
852
+ // all messages that exist.
853
+ options = {
854
+ user_ids: [this.knock.userId],
855
+ engagement_status: this.defaultOptions.status !== "all" ? this.defaultOptions.status : undefined,
856
+ archived: this.defaultOptions.archived,
857
+ has_tenant: this.defaultOptions.has_tenant,
858
+ tenants: this.defaultOptions.tenant ? [this.defaultOptions.tenant] : undefined
859
+ };
860
+ _context15.next = 3;
861
+ return this.apiClient.makeRequest({
862
+ method: "POST",
863
+ url: "/v1/channels/".concat(this.feedId, "/messages/bulk/").concat(type),
864
+ data: options
865
+ });
866
+ case 3:
867
+ return _context15.abrupt("return", _context15.sent);
868
+ case 4:
869
+ case "end":
870
+ return _context15.stop();
994
871
  }
995
872
  }, _callee15, this);
996
873
  }));
997
-
998
874
  function makeBulkStatusUpdate(_x11) {
999
875
  return _makeBulkStatusUpdate.apply(this, arguments);
1000
876
  }
1001
-
1002
877
  return makeBulkStatusUpdate;
1003
878
  }()
1004
879
  }, {
@@ -1007,10 +882,10 @@ var Feed = /*#__PURE__*/function () {
1007
882
  // The broadcastChannel may not be available in non-browser environments
1008
883
  if (!this.broadcastChannel) {
1009
884
  return;
1010
- } // Here we stringify our payload and try and send as JSON such that we
1011
- // don't get any `An object could not be cloned` errors when trying to broadcast
1012
-
885
+ }
1013
886
 
887
+ // Here we stringify our payload and try and send as JSON such that we
888
+ // don't get any `An object could not be cloned` errors when trying to broadcast
1014
889
  try {
1015
890
  var stringifiedPayload = JSON.parse(JSON.stringify(payload));
1016
891
  this.broadcastChannel.postMessage({
@@ -1021,10 +896,42 @@ var Feed = /*#__PURE__*/function () {
1021
896
  console.warn("Could not broadcast ".concat(type, ", got error: ").concat(e));
1022
897
  }
1023
898
  }
899
+
900
+ /**
901
+ * Listen for changes to document visibility and automatically disconnect
902
+ * or reconnect the socket after a delay
903
+ */
904
+ }, {
905
+ key: "setupAutoSocketManager",
906
+ value: function setupAutoSocketManager(autoManageSocketConnectionDelay) {
907
+ var _this3 = this;
908
+ var disconnectDelay = autoManageSocketConnectionDelay !== null && autoManageSocketConnectionDelay !== void 0 ? autoManageSocketConnectionDelay : DEFAULT_DISCONNECT_DELAY;
909
+ document.addEventListener("visibilitychange", function () {
910
+ if (document.visibilityState === "hidden") {
911
+ // When the tab is hidden, clean up the socket connection after a delay
912
+ _this3.disconnectTimer = setTimeout(function () {
913
+ _this3.apiClient.disconnectSocket();
914
+ _this3.disconnectTimer = null;
915
+ }, disconnectDelay);
916
+ } else if (document.visibilityState === "visible") {
917
+ var _this3$apiClient$sock;
918
+ // When the tab is visible, clear the disconnect timer if active to cancel disconnecting
919
+ // This handles cases where the tab is only briefly hidden to avoid unnecessary disconnects
920
+ if (_this3.disconnectTimer) {
921
+ clearTimeout(_this3.disconnectTimer);
922
+ _this3.disconnectTimer = null;
923
+ }
924
+
925
+ // If the socket is not connected, try to reconnect
926
+ if (!((_this3$apiClient$sock = _this3.apiClient.socket) !== null && _this3$apiClient$sock !== void 0 && _this3$apiClient$sock.isConnected())) {
927
+ _this3.apiClient.reconnectSocket();
928
+ _this3.fetch();
929
+ }
930
+ }
931
+ });
932
+ }
1024
933
  }]);
1025
934
  return Feed;
1026
935
  }();
1027
-
1028
- var _default = Feed;
1029
- exports["default"] = _default;
936
+ var _default = exports["default"] = Feed;
1030
937
  //# sourceMappingURL=feed.js.map