@knocklabs/client 0.8.2 → 0.8.4

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.
@@ -27,11 +27,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
27
27
 
28
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
29
 
30
- function invertStatus(status) {
31
- return status.startsWith("un") ? status.substring(2, status.length) : "un".concat(status);
32
- } // Default options to apply
33
-
34
-
30
+ // Default options to apply
35
31
  var feedClientDefaults = {
36
32
  archived: "exclude"
37
33
  };
@@ -49,6 +45,7 @@ var Feed = /*#__PURE__*/function () {
49
45
  (0, _defineProperty2["default"])(this, "channel", void 0);
50
46
  (0, _defineProperty2["default"])(this, "broadcaster", void 0);
51
47
  (0, _defineProperty2["default"])(this, "defaultOptions", void 0);
48
+ (0, _defineProperty2["default"])(this, "broadcastChannel", void 0);
52
49
  (0, _defineProperty2["default"])(this, "store", void 0);
53
50
  this.apiClient = knock.client();
54
51
  this.feedId = feedId;
@@ -62,7 +59,10 @@ var Feed = /*#__PURE__*/function () {
62
59
  this.channel = this.apiClient.socket.channel("feeds:".concat(this.userFeedId), this.defaultOptions);
63
60
  this.channel.on("new-message", function (resp) {
64
61
  return _this.onNewMessageReceived(resp);
65
- });
62
+ }); // Attempt to bind to listen to other events from this feed in different tabs
63
+ // Note: here we ensure `self` is available (it's not in server rendered envs)
64
+
65
+ this.broadcastChannel = self && "BroadcastChannel" in self ? new BroadcastChannel("knock:feed:".concat(this.userFeedId)) : null;
66
66
  }
67
67
  /**
68
68
  * Cleans up a feed instance by destroying the store and disconnecting
@@ -77,6 +77,10 @@ var Feed = /*#__PURE__*/function () {
77
77
  this.broadcaster.removeAllListeners();
78
78
  this.channel.off("new-message");
79
79
  this.store.destroy();
80
+
81
+ if (this.broadcastChannel) {
82
+ this.broadcastChannel.close();
83
+ }
80
84
  }
81
85
  /*
82
86
  Initializes a real-time connection to Knock, connecting the websocket for the
@@ -86,6 +90,8 @@ var Feed = /*#__PURE__*/function () {
86
90
  }, {
87
91
  key: "listenForUpdates",
88
92
  value: function listenForUpdates() {
93
+ var _this2 = this;
94
+
89
95
  // Connect the socket only if we don't already have a connection
90
96
  if (!this.apiClient.socket.isConnected()) {
91
97
  this.apiClient.socket.connect();
@@ -94,6 +100,32 @@ var Feed = /*#__PURE__*/function () {
94
100
 
95
101
  if (["closed", "errored"].includes(this.channel.state)) {
96
102
  this.channel.join();
103
+ } // Opt into receiving updates from _other tabs for the same user / feed_ via the broadcast
104
+ // channel (iff it's enabled and exists)
105
+
106
+
107
+ if (this.broadcastChannel && this.defaultOptions.__experimentalCrossBrowserUpdates === true) {
108
+ this.broadcastChannel.onmessage = function (e) {
109
+ switch (e.data.type) {
110
+ case "items:archived":
111
+ case "items:unarchived":
112
+ case "items:seen":
113
+ case "items:unseen":
114
+ case "items:read":
115
+ case "items:unread":
116
+ case "items:all_read":
117
+ case "items:all_seen":
118
+ case "items:all_archived":
119
+ // When items are updated in any other tab, simply refetch to get the latest state
120
+ // to make sure that the state gets updated accordingly. In the future here we could
121
+ // maybe do this optimistically without the fetch.
122
+ return _this2.fetch();
123
+ break;
124
+
125
+ default:
126
+ return null;
127
+ }
128
+ };
97
129
  }
98
130
  }
99
131
  /* Binds a handler to be invoked when event occurs */
@@ -143,24 +175,104 @@ var Feed = /*#__PURE__*/function () {
143
175
  return markAsSeen;
144
176
  }()
145
177
  }, {
146
- key: "markAsUnseen",
178
+ key: "markAllAsSeen",
147
179
  value: function () {
148
- var _markAsUnseen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(itemOrItems) {
180
+ var _markAllAsSeen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2() {
181
+ var _this$store, getState, setState, _getState, metadata, items, isViewingOnlyUnseen, attrs, itemIds, result;
182
+
149
183
  return _regenerator["default"].wrap(function _callee2$(_context2) {
150
184
  while (1) {
151
185
  switch (_context2.prev = _context2.next) {
186
+ case 0:
187
+ // To mark all of the messages as seen we:
188
+ // 1. Optimistically update *everything* we have in the store
189
+ // 2. We decrement the `unseen_count` to zero optimistically
190
+ // 3. We issue the API call to the endpoint
191
+ //
192
+ // Note: there is the potential for a race condition here because the bulk
193
+ // update is an async method, so if a new message comes in during this window before
194
+ // the update has been processed we'll effectively reset the `unseen_count` to be what it was.
195
+ //
196
+ // Note: here we optimistically handle the case whereby the feed is scoped to show only `unseen`
197
+ // items by removing everything from view.
198
+ _this$store = this.store, getState = _this$store.getState, setState = _this$store.setState;
199
+ _getState = getState(), metadata = _getState.metadata, items = _getState.items;
200
+ isViewingOnlyUnseen = this.defaultOptions.status === "unseen"; // If we're looking at the unseen view, then we want to remove all of the items optimistically
201
+ // from the store given that nothing should be visible. We do this by resetting the store state
202
+ // and setting the current metadata counts to 0
203
+
204
+ if (isViewingOnlyUnseen) {
205
+ setState(function (store) {
206
+ return store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
207
+ total_count: 0,
208
+ unseen_count: 0
209
+ }));
210
+ });
211
+ } else {
212
+ // Otherwise we want to update the metadata and mark all of the items in the store as seen
213
+ setState(function (store) {
214
+ return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, {
215
+ unseen_count: 0
216
+ }));
217
+ });
218
+ attrs = {
219
+ seen_at: new Date().toISOString()
220
+ };
221
+ itemIds = items.map(function (item) {
222
+ return item.id;
223
+ });
224
+ setState(function (store) {
225
+ return store.setItemAttrs(itemIds, attrs);
226
+ });
227
+ } // Issue the API request to the bulk status change API
228
+
229
+
230
+ _context2.next = 6;
231
+ return this.makeBulkStatusUpdate("seen");
232
+
233
+ case 6:
234
+ result = _context2.sent;
235
+ this.broadcaster.emit("items:all_seen", {
236
+ items: items
237
+ });
238
+ this.broadcastOverChannel("items:all_seen", {
239
+ items: items
240
+ });
241
+ return _context2.abrupt("return", result);
242
+
243
+ case 10:
244
+ case "end":
245
+ return _context2.stop();
246
+ }
247
+ }
248
+ }, _callee2, this);
249
+ }));
250
+
251
+ function markAllAsSeen() {
252
+ return _markAllAsSeen.apply(this, arguments);
253
+ }
254
+
255
+ return markAllAsSeen;
256
+ }()
257
+ }, {
258
+ key: "markAsUnseen",
259
+ value: function () {
260
+ var _markAsUnseen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(itemOrItems) {
261
+ return _regenerator["default"].wrap(function _callee3$(_context3) {
262
+ while (1) {
263
+ switch (_context3.prev = _context3.next) {
152
264
  case 0:
153
265
  this.optimisticallyPerformStatusUpdate(itemOrItems, "unseen", {
154
266
  seen_at: null
155
267
  }, "unseen_count");
156
- return _context2.abrupt("return", this.makeStatusUpdate(itemOrItems, "unseen"));
268
+ return _context3.abrupt("return", this.makeStatusUpdate(itemOrItems, "unseen"));
157
269
 
158
270
  case 2:
159
271
  case "end":
160
- return _context2.stop();
272
+ return _context3.stop();
161
273
  }
162
274
  }
163
- }, _callee2, this);
275
+ }, _callee3, this);
164
276
  }));
165
277
 
166
278
  function markAsUnseen(_x2) {
@@ -172,24 +284,24 @@ var Feed = /*#__PURE__*/function () {
172
284
  }, {
173
285
  key: "markAsRead",
174
286
  value: function () {
175
- var _markAsRead = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(itemOrItems) {
287
+ var _markAsRead = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(itemOrItems) {
176
288
  var now;
177
- return _regenerator["default"].wrap(function _callee3$(_context3) {
289
+ return _regenerator["default"].wrap(function _callee4$(_context4) {
178
290
  while (1) {
179
- switch (_context3.prev = _context3.next) {
291
+ switch (_context4.prev = _context4.next) {
180
292
  case 0:
181
293
  now = new Date().toISOString();
182
294
  this.optimisticallyPerformStatusUpdate(itemOrItems, "read", {
183
295
  read_at: now
184
296
  }, "unread_count");
185
- return _context3.abrupt("return", this.makeStatusUpdate(itemOrItems, "read"));
297
+ return _context4.abrupt("return", this.makeStatusUpdate(itemOrItems, "read"));
186
298
 
187
299
  case 3:
188
300
  case "end":
189
- return _context3.stop();
301
+ return _context4.stop();
190
302
  }
191
303
  }
192
- }, _callee3, this);
304
+ }, _callee4, this);
193
305
  }));
194
306
 
195
307
  function markAsRead(_x3) {
@@ -198,25 +310,105 @@ var Feed = /*#__PURE__*/function () {
198
310
 
199
311
  return markAsRead;
200
312
  }()
313
+ }, {
314
+ key: "markAllAsRead",
315
+ value: function () {
316
+ var _markAllAsRead = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5() {
317
+ var _this$store2, getState, setState, _getState2, metadata, items, isViewingOnlyUnread, attrs, itemIds, result;
318
+
319
+ return _regenerator["default"].wrap(function _callee5$(_context5) {
320
+ while (1) {
321
+ switch (_context5.prev = _context5.next) {
322
+ case 0:
323
+ // To mark all of the messages as read we:
324
+ // 1. Optimistically update *everything* we have in the store
325
+ // 2. We decrement the `unread_count` to zero optimistically
326
+ // 3. We issue the API call to the endpoint
327
+ //
328
+ // Note: there is the potential for a race condition here because the bulk
329
+ // update is an async method, so if a new message comes in during this window before
330
+ // the update has been processed we'll effectively reset the `unread_count` to be what it was.
331
+ //
332
+ // Note: here we optimistically handle the case whereby the feed is scoped to show only `unread`
333
+ // items by removing everything from view.
334
+ _this$store2 = this.store, getState = _this$store2.getState, setState = _this$store2.setState;
335
+ _getState2 = getState(), metadata = _getState2.metadata, items = _getState2.items;
336
+ isViewingOnlyUnread = this.defaultOptions.status === "unread"; // If we're looking at the unread view, then we want to remove all of the items optimistically
337
+ // from the store given that nothing should be visible. We do this by resetting the store state
338
+ // and setting the current metadata counts to 0
339
+
340
+ if (isViewingOnlyUnread) {
341
+ setState(function (store) {
342
+ return store.resetStore(_objectSpread(_objectSpread({}, metadata), {}, {
343
+ total_count: 0,
344
+ unread_count: 0
345
+ }));
346
+ });
347
+ } else {
348
+ // Otherwise we want to update the metadata and mark all of the items in the store as seen
349
+ setState(function (store) {
350
+ return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, {
351
+ unread_count: 0
352
+ }));
353
+ });
354
+ attrs = {
355
+ read_at: new Date().toISOString()
356
+ };
357
+ itemIds = items.map(function (item) {
358
+ return item.id;
359
+ });
360
+ setState(function (store) {
361
+ return store.setItemAttrs(itemIds, attrs);
362
+ });
363
+ } // Issue the API request to the bulk status change API
364
+
365
+
366
+ _context5.next = 6;
367
+ return this.makeBulkStatusUpdate("read");
368
+
369
+ case 6:
370
+ result = _context5.sent;
371
+ this.broadcaster.emit("items:all_read", {
372
+ items: items
373
+ });
374
+ this.broadcastOverChannel("items:all_read", {
375
+ items: items
376
+ });
377
+ return _context5.abrupt("return", result);
378
+
379
+ case 10:
380
+ case "end":
381
+ return _context5.stop();
382
+ }
383
+ }
384
+ }, _callee5, this);
385
+ }));
386
+
387
+ function markAllAsRead() {
388
+ return _markAllAsRead.apply(this, arguments);
389
+ }
390
+
391
+ return markAllAsRead;
392
+ }()
201
393
  }, {
202
394
  key: "markAsUnread",
203
395
  value: function () {
204
- var _markAsUnread = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(itemOrItems) {
205
- return _regenerator["default"].wrap(function _callee4$(_context4) {
396
+ var _markAsUnread = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6(itemOrItems) {
397
+ return _regenerator["default"].wrap(function _callee6$(_context6) {
206
398
  while (1) {
207
- switch (_context4.prev = _context4.next) {
399
+ switch (_context6.prev = _context6.next) {
208
400
  case 0:
209
401
  this.optimisticallyPerformStatusUpdate(itemOrItems, "unread", {
210
402
  read_at: null
211
403
  }, "unread_count");
212
- return _context4.abrupt("return", this.makeStatusUpdate(itemOrItems, "unread"));
404
+ return _context6.abrupt("return", this.makeStatusUpdate(itemOrItems, "unread"));
213
405
 
214
406
  case 2:
215
407
  case "end":
216
- return _context4.stop();
408
+ return _context6.stop();
217
409
  }
218
410
  }
219
- }, _callee4, this);
411
+ }, _callee6, this);
220
412
  }));
221
413
 
222
414
  function markAsUnread(_x4) {
@@ -235,14 +427,14 @@ var Feed = /*#__PURE__*/function () {
235
427
  }, {
236
428
  key: "markAsArchived",
237
429
  value: function () {
238
- var _markAsArchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5(itemOrItems) {
239
- var _this$store, getState, setState, state, shouldOptimisticallyRemoveItems, normalizedItems, itemIds, unseenCount, unreadCount, updatedMetadata, entriesToSet;
430
+ var _markAsArchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee7(itemOrItems) {
431
+ var _this$store3, getState, setState, state, shouldOptimisticallyRemoveItems, normalizedItems, itemIds, unseenCount, unreadCount, updatedMetadata, entriesToSet;
240
432
 
241
- return _regenerator["default"].wrap(function _callee5$(_context5) {
433
+ return _regenerator["default"].wrap(function _callee7$(_context7) {
242
434
  while (1) {
243
- switch (_context5.prev = _context5.next) {
435
+ switch (_context7.prev = _context7.next) {
244
436
  case 0:
245
- _this$store = this.store, getState = _this$store.getState, setState = _this$store.setState;
437
+ _this$store3 = this.store, getState = _this$store3.getState, setState = _this$store3.setState;
246
438
  state = getState();
247
439
  shouldOptimisticallyRemoveItems = this.defaultOptions.archived === "exclude";
248
440
  normalizedItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
@@ -250,7 +442,7 @@ var Feed = /*#__PURE__*/function () {
250
442
  return item.id;
251
443
  });
252
444
  /*
253
- In the proceeding code here we want to optimistically update counts and items
445
+ In the code here we want to optimistically update counts and items
254
446
  that are persisted such that we can display updates immediately on the feed
255
447
  without needing to make a network request.
256
448
  Note: right now this does *not* take into account offline handling or any extensive retry
@@ -301,14 +493,14 @@ var Feed = /*#__PURE__*/function () {
301
493
  });
302
494
  }
303
495
 
304
- return _context5.abrupt("return", this.makeStatusUpdate(itemOrItems, "archived"));
496
+ return _context7.abrupt("return", this.makeStatusUpdate(itemOrItems, "archived"));
305
497
 
306
498
  case 7:
307
499
  case "end":
308
- return _context5.stop();
500
+ return _context7.stop();
309
501
  }
310
502
  }
311
- }, _callee5, this);
503
+ }, _callee7, this);
312
504
  }));
313
505
 
314
506
  function markAsArchived(_x5) {
@@ -317,25 +509,89 @@ var Feed = /*#__PURE__*/function () {
317
509
 
318
510
  return markAsArchived;
319
511
  }()
512
+ }, {
513
+ key: "markAllAsArchived",
514
+ value: function () {
515
+ var _markAllAsArchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee8() {
516
+ var _this$store4, setState, getState, _getState3, items, shouldOptimisticallyRemoveItems, result;
517
+
518
+ return _regenerator["default"].wrap(function _callee8$(_context8) {
519
+ while (1) {
520
+ switch (_context8.prev = _context8.next) {
521
+ case 0:
522
+ // Note: there is the potential for a race condition here because the bulk
523
+ // update is an async method, so if a new message comes in during this window before
524
+ // the update has been processed we'll effectively reset the `unseen_count` to be what it was.
525
+ _this$store4 = this.store, setState = _this$store4.setState, getState = _this$store4.getState;
526
+ _getState3 = getState(), items = _getState3.items; // Here if we're looking at a feed that excludes all of the archived items by default then we
527
+ // will want to optimistically remove all of the items from the feed as they are now all excluded
528
+
529
+ shouldOptimisticallyRemoveItems = this.defaultOptions.archived === "exclude";
530
+
531
+ if (shouldOptimisticallyRemoveItems) {
532
+ // Reset the store to clear out all of items and reset the badge count
533
+ setState(function (store) {
534
+ return store.resetStore();
535
+ });
536
+ } else {
537
+ // Mark all the entries being updated as archived either way so the state is correct
538
+ setState(function (store) {
539
+ var itemIds = items.map(function (i) {
540
+ return i.id;
541
+ });
542
+ store.setItemAttrs(itemIds, {
543
+ archived_at: new Date().toISOString()
544
+ });
545
+ });
546
+ } // Issue the API request to the bulk status change API
547
+
548
+
549
+ _context8.next = 6;
550
+ return this.makeBulkStatusUpdate("archive");
551
+
552
+ case 6:
553
+ result = _context8.sent;
554
+ this.broadcaster.emit("items:all_archived", {
555
+ items: items
556
+ });
557
+ this.broadcastOverChannel("items:all_archived", {
558
+ items: items
559
+ });
560
+ return _context8.abrupt("return", result);
561
+
562
+ case 10:
563
+ case "end":
564
+ return _context8.stop();
565
+ }
566
+ }
567
+ }, _callee8, this);
568
+ }));
569
+
570
+ function markAllAsArchived() {
571
+ return _markAllAsArchived.apply(this, arguments);
572
+ }
573
+
574
+ return markAllAsArchived;
575
+ }()
320
576
  }, {
321
577
  key: "markAsUnarchived",
322
578
  value: function () {
323
- var _markAsUnarchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6(itemOrItems) {
324
- return _regenerator["default"].wrap(function _callee6$(_context6) {
579
+ var _markAsUnarchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee9(itemOrItems) {
580
+ return _regenerator["default"].wrap(function _callee9$(_context9) {
325
581
  while (1) {
326
- switch (_context6.prev = _context6.next) {
582
+ switch (_context9.prev = _context9.next) {
327
583
  case 0:
328
584
  this.optimisticallyPerformStatusUpdate(itemOrItems, "unarchived", {
329
585
  archived_at: null
330
586
  });
331
- return _context6.abrupt("return", this.makeStatusUpdate(itemOrItems, "unarchived"));
587
+ return _context9.abrupt("return", this.makeStatusUpdate(itemOrItems, "unarchived"));
332
588
 
333
589
  case 2:
334
590
  case "end":
335
- return _context6.stop();
591
+ return _context9.stop();
336
592
  }
337
593
  }
338
- }, _callee6, this);
594
+ }, _callee9, this);
339
595
  }));
340
596
 
341
597
  function markAsUnarchived(_x6) {
@@ -349,12 +605,12 @@ var Feed = /*#__PURE__*/function () {
349
605
  }, {
350
606
  key: "fetch",
351
607
  value: function () {
352
- var _fetch = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee7() {
608
+ var _fetch = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee10() {
353
609
  var options,
354
- _this$store2,
610
+ _this$store5,
355
611
  setState,
356
612
  getState,
357
- _getState,
613
+ _getState4,
358
614
  networkStatus,
359
615
  queryParams,
360
616
  result,
@@ -363,22 +619,22 @@ var Feed = /*#__PURE__*/function () {
363
619
  _opts,
364
620
  feedEventType,
365
621
  eventPayload,
366
- _args7 = arguments;
622
+ _args10 = arguments;
367
623
 
368
- return _regenerator["default"].wrap(function _callee7$(_context7) {
624
+ return _regenerator["default"].wrap(function _callee10$(_context10) {
369
625
  while (1) {
370
- switch (_context7.prev = _context7.next) {
626
+ switch (_context10.prev = _context10.next) {
371
627
  case 0:
372
- options = _args7.length > 0 && _args7[0] !== undefined ? _args7[0] : {};
373
- _this$store2 = this.store, setState = _this$store2.setState, getState = _this$store2.getState;
374
- _getState = getState(), networkStatus = _getState.networkStatus; // If there's an existing request in flight, then do nothing
628
+ options = _args10.length > 0 && _args10[0] !== undefined ? _args10[0] : {};
629
+ _this$store5 = this.store, setState = _this$store5.setState, getState = _this$store5.getState;
630
+ _getState4 = getState(), networkStatus = _getState4.networkStatus; // If there's an existing request in flight, then do nothing
375
631
 
376
632
  if (!(0, _networkStatus.isRequestInFlight)(networkStatus)) {
377
- _context7.next = 5;
633
+ _context10.next = 5;
378
634
  break;
379
635
  }
380
636
 
381
- return _context7.abrupt("return");
637
+ return _context10.abrupt("return");
382
638
 
383
639
  case 5:
384
640
  // Set the loading type based on the request type it is
@@ -388,8 +644,13 @@ var Feed = /*#__PURE__*/function () {
388
644
  return store.setNetworkStatus((_options$__loadingTyp = options.__loadingType) !== null && _options$__loadingTyp !== void 0 ? _options$__loadingTyp : _networkStatus.NetworkStatus.loading);
389
645
  }); // Always include the default params, if they have been set
390
646
 
391
- queryParams = _objectSpread(_objectSpread({}, this.defaultOptions), options);
392
- _context7.next = 9;
647
+ queryParams = _objectSpread(_objectSpread(_objectSpread({}, this.defaultOptions), options), {}, {
648
+ // Unset options that should not be sent to the API
649
+ __loadingType: undefined,
650
+ __fetchSource: undefined,
651
+ __experimentalCrossBrowserUpdates: undefined
652
+ });
653
+ _context10.next = 9;
393
654
  return this.apiClient.makeRequest({
394
655
  method: "GET",
395
656
  url: "/v1/users/".concat(this.knock.userId, "/feeds/").concat(this.feedId),
@@ -397,17 +658,17 @@ var Feed = /*#__PURE__*/function () {
397
658
  });
398
659
 
399
660
  case 9:
400
- result = _context7.sent;
661
+ result = _context10.sent;
401
662
 
402
663
  if (!(result.statusCode === "error" || !result.body)) {
403
- _context7.next = 13;
664
+ _context10.next = 13;
404
665
  break;
405
666
  }
406
667
 
407
668
  setState(function (store) {
408
669
  return store.setNetworkStatus(_networkStatus.NetworkStatus.error);
409
670
  });
410
- return _context7.abrupt("return", {
671
+ return _context10.abrupt("return", {
411
672
  status: result.statusCode,
412
673
  data: result.error || result.body
413
674
  });
@@ -451,17 +712,17 @@ var Feed = /*#__PURE__*/function () {
451
712
  event: feedEventType
452
713
  };
453
714
  this.broadcast(eventPayload.event, eventPayload);
454
- return _context7.abrupt("return", {
715
+ return _context10.abrupt("return", {
455
716
  data: response,
456
717
  status: result.statusCode
457
718
  });
458
719
 
459
720
  case 20:
460
721
  case "end":
461
- return _context7.stop();
722
+ return _context10.stop();
462
723
  }
463
724
  }
464
- }, _callee7, this);
725
+ }, _callee10, this);
465
726
  }));
466
727
 
467
728
  function fetch() {
@@ -473,23 +734,23 @@ var Feed = /*#__PURE__*/function () {
473
734
  }, {
474
735
  key: "fetchNextPage",
475
736
  value: function () {
476
- var _fetchNextPage = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee8() {
477
- var getState, _getState2, pageInfo;
737
+ var _fetchNextPage = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee11() {
738
+ var getState, _getState5, pageInfo;
478
739
 
479
- return _regenerator["default"].wrap(function _callee8$(_context8) {
740
+ return _regenerator["default"].wrap(function _callee11$(_context11) {
480
741
  while (1) {
481
- switch (_context8.prev = _context8.next) {
742
+ switch (_context11.prev = _context11.next) {
482
743
  case 0:
483
744
  // Attempts to fetch the next page of results (if we have any)
484
745
  getState = this.store.getState;
485
- _getState2 = getState(), pageInfo = _getState2.pageInfo;
746
+ _getState5 = getState(), pageInfo = _getState5.pageInfo;
486
747
 
487
748
  if (pageInfo.after) {
488
- _context8.next = 4;
749
+ _context11.next = 4;
489
750
  break;
490
751
  }
491
752
 
492
- return _context8.abrupt("return");
753
+ return _context11.abrupt("return");
493
754
 
494
755
  case 4:
495
756
  this.fetch({
@@ -499,10 +760,10 @@ var Feed = /*#__PURE__*/function () {
499
760
 
500
761
  case 5:
501
762
  case "end":
502
- return _context8.stop();
763
+ return _context11.stop();
503
764
  }
504
765
  }
505
- }, _callee8, this);
766
+ }, _callee11, this);
506
767
  }));
507
768
 
508
769
  function fetchNextPage() {
@@ -520,17 +781,17 @@ var Feed = /*#__PURE__*/function () {
520
781
  }, {
521
782
  key: "onNewMessageReceived",
522
783
  value: function () {
523
- var _onNewMessageReceived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee9(_ref) {
524
- var metadata, _this$store3, getState, setState, _getState3, items, currentHead;
784
+ var _onNewMessageReceived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee12(_ref) {
785
+ var metadata, _this$store6, getState, setState, _getState6, items, currentHead;
525
786
 
526
- return _regenerator["default"].wrap(function _callee9$(_context9) {
787
+ return _regenerator["default"].wrap(function _callee12$(_context12) {
527
788
  while (1) {
528
- switch (_context9.prev = _context9.next) {
789
+ switch (_context12.prev = _context12.next) {
529
790
  case 0:
530
791
  metadata = _ref.metadata;
531
792
  // Handle the new message coming in
532
- _this$store3 = this.store, getState = _this$store3.getState, setState = _this$store3.setState;
533
- _getState3 = getState(), items = _getState3.items;
793
+ _this$store6 = this.store, getState = _this$store6.getState, setState = _this$store6.setState;
794
+ _getState6 = getState(), items = _getState6.items;
534
795
  currentHead = items[0]; // Optimistically set the badge counts
535
796
 
536
797
  setState(function (state) {
@@ -544,10 +805,10 @@ var Feed = /*#__PURE__*/function () {
544
805
 
545
806
  case 6:
546
807
  case "end":
547
- return _context9.stop();
808
+ return _context12.stop();
548
809
  }
549
810
  }
550
- }, _callee9, this);
811
+ }, _callee12, this);
551
812
  }));
552
813
 
553
814
  function onNewMessageReceived(_x7) {
@@ -564,16 +825,16 @@ var Feed = /*#__PURE__*/function () {
564
825
  }, {
565
826
  key: "optimisticallyPerformStatusUpdate",
566
827
  value: function optimisticallyPerformStatusUpdate(itemOrItems, type, attrs, badgeCountAttr) {
567
- var _this$store4 = this.store,
568
- getState = _this$store4.getState,
569
- setState = _this$store4.setState;
828
+ var _this$store7 = this.store,
829
+ getState = _this$store7.getState,
830
+ setState = _this$store7.setState;
570
831
  var itemIds = Array.isArray(itemOrItems) ? itemOrItems.map(function (item) {
571
832
  return item.id;
572
833
  }) : [itemOrItems.id];
573
834
 
574
835
  if (badgeCountAttr) {
575
- var _getState4 = getState(),
576
- metadata = _getState4.metadata; // Tnis is a hack to determine the direction of whether we're
836
+ var _getState7 = getState(),
837
+ metadata = _getState7.metadata; // Tnis is a hack to determine the direction of whether we're
577
838
  // adding or removing from the badge count
578
839
 
579
840
 
@@ -591,21 +852,18 @@ var Feed = /*#__PURE__*/function () {
591
852
  }, {
592
853
  key: "makeStatusUpdate",
593
854
  value: function () {
594
- var _makeStatusUpdate = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee10(itemOrItems, type) {
595
- var itemIds, result;
596
- return _regenerator["default"].wrap(function _callee10$(_context10) {
855
+ var _makeStatusUpdate = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee13(itemOrItems, type) {
856
+ var items, itemIds, result;
857
+ return _regenerator["default"].wrap(function _callee13$(_context13) {
597
858
  while (1) {
598
- switch (_context10.prev = _context10.next) {
859
+ switch (_context13.prev = _context13.next) {
599
860
  case 0:
600
- if (!Array.isArray(itemOrItems)) {
601
- _context10.next = 5;
602
- break;
603
- }
604
-
605
- itemIds = itemOrItems.map(function (item) {
861
+ // Always treat items as a batch to use the corresponding batch endpoint
862
+ items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
863
+ itemIds = items.map(function (item) {
606
864
  return item.id;
607
865
  });
608
- _context10.next = 4;
866
+ _context13.next = 4;
609
867
  return this.apiClient.makeRequest({
610
868
  method: "POST",
611
869
  url: "/v1/messages/batch/".concat(type),
@@ -615,48 +873,95 @@ var Feed = /*#__PURE__*/function () {
615
873
  });
616
874
 
617
875
  case 4:
618
- return _context10.abrupt("return", _context10.sent);
619
-
620
- case 5:
621
- if (!type.startsWith("un")) {
622
- _context10.next = 9;
623
- break;
624
- }
625
-
626
- _context10.next = 8;
627
- return this.apiClient.makeRequest({
628
- method: "DELETE",
629
- url: "/v1/messages/".concat(itemOrItems.id, "/").concat(invertStatus(type))
876
+ result = _context13.sent;
877
+ // Emit the event that these items had their statuses changed
878
+ // Note: we do this after the update to ensure that the server event actually completed
879
+ this.broadcaster.emit("items:".concat(type), {
880
+ items: items
630
881
  });
882
+ this.broadcastOverChannel("items:".concat(type), {
883
+ items: items
884
+ });
885
+ return _context13.abrupt("return", result);
631
886
 
632
887
  case 8:
633
- return _context10.abrupt("return", _context10.sent);
888
+ case "end":
889
+ return _context13.stop();
890
+ }
891
+ }
892
+ }, _callee13, this);
893
+ }));
634
894
 
635
- case 9:
636
- _context10.next = 11;
895
+ function makeStatusUpdate(_x8, _x9) {
896
+ return _makeStatusUpdate.apply(this, arguments);
897
+ }
898
+
899
+ return makeStatusUpdate;
900
+ }()
901
+ }, {
902
+ key: "makeBulkStatusUpdate",
903
+ value: function () {
904
+ var _makeBulkStatusUpdate = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee14(type) {
905
+ var options;
906
+ return _regenerator["default"].wrap(function _callee14$(_context14) {
907
+ while (1) {
908
+ switch (_context14.prev = _context14.next) {
909
+ case 0:
910
+ // The base scope for the call should take into account all of the options currently
911
+ // set on the feed, as well as being scoped for the current user. We do this so that
912
+ // we ONLY make changes to the messages that are currently in view on this feed, and not
913
+ // all messages that exist.
914
+ options = {
915
+ user_ids: [this.knock.userId],
916
+ engagement_status: this.defaultOptions.status !== "all" ? this.defaultOptions.status : undefined,
917
+ archived: this.defaultOptions.archived,
918
+ has_tenant: this.defaultOptions.has_tenant,
919
+ tenants: this.defaultOptions.tenant ? [this.defaultOptions.tenant] : undefined
920
+ };
921
+ _context14.next = 3;
637
922
  return this.apiClient.makeRequest({
638
- method: "PUT",
639
- url: "/v1/messages/".concat(itemOrItems.id, "/").concat(type)
923
+ method: "POST",
924
+ url: "/v1/channels/".concat(this.feedId, "/messages/bulk/").concat(type),
925
+ data: options
640
926
  });
641
927
 
642
- case 11:
643
- result = _context10.sent;
644
- return _context10.abrupt("return", result);
928
+ case 3:
929
+ return _context14.abrupt("return", _context14.sent);
645
930
 
646
- case 13:
931
+ case 4:
647
932
  case "end":
648
- return _context10.stop();
933
+ return _context14.stop();
649
934
  }
650
935
  }
651
- }, _callee10, this);
936
+ }, _callee14, this);
652
937
  }));
653
938
 
654
- function makeStatusUpdate(_x8, _x9) {
655
- return _makeStatusUpdate.apply(this, arguments);
939
+ function makeBulkStatusUpdate(_x10) {
940
+ return _makeBulkStatusUpdate.apply(this, arguments);
656
941
  }
657
942
 
658
- return makeStatusUpdate;
943
+ return makeBulkStatusUpdate;
659
944
  }()
945
+ }, {
946
+ key: "broadcastOverChannel",
947
+ value: function broadcastOverChannel(type, payload) {
948
+ // The broadcastChannel may not be available in non-browser environments
949
+ if (!this.broadcastChannel) {
950
+ return;
951
+ } // Here we stringify our payload and try and send as JSON such that we
952
+ // don't get any `An object could not be cloned` errors when trying to broadcast
953
+
954
+
955
+ try {
956
+ var stringifiedPayload = JSON.parse(JSON.stringify(payload));
957
+ this.broadcastChannel.postMessage({
958
+ type: type,
959
+ payload: stringifiedPayload
960
+ });
961
+ } catch (e) {
962
+ console.warn("Could not broadcast ".concat(type, ", got error: ").concat(e));
963
+ }
964
+ }
660
965
  }]);
661
966
  return Feed;
662
967
  }();