@knocklabs/client 0.8.18 → 0.8.19

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 (121) hide show
  1. package/dist/api.d.ts +25 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +84 -0
  4. package/dist/cjs/api.js +2 -133
  5. package/dist/cjs/api.js.map +1 -1
  6. package/dist/cjs/clients/feed/feed.js +2 -939
  7. package/dist/cjs/clients/feed/feed.js.map +1 -1
  8. package/dist/cjs/clients/feed/index.js +2 -34
  9. package/dist/cjs/clients/feed/index.js.map +1 -1
  10. package/dist/cjs/clients/feed/store.js +2 -111
  11. package/dist/cjs/clients/feed/store.js.map +1 -1
  12. package/dist/cjs/clients/feed/utils.js +2 -26
  13. package/dist/cjs/clients/feed/utils.js.map +1 -1
  14. package/dist/cjs/clients/preferences/index.js +2 -373
  15. package/dist/cjs/clients/preferences/index.js.map +1 -1
  16. package/dist/cjs/clients/users/index.js +2 -185
  17. package/dist/cjs/clients/users/index.js.map +1 -1
  18. package/dist/cjs/index.js +2 -102
  19. package/dist/cjs/index.js.map +1 -1
  20. package/dist/cjs/knock.js +6 -89
  21. package/dist/cjs/knock.js.map +1 -1
  22. package/dist/cjs/networkStatus.js +2 -18
  23. package/dist/cjs/networkStatus.js.map +1 -1
  24. package/dist/clients/feed/feed.d.ts +64 -0
  25. package/dist/clients/feed/feed.d.ts.map +1 -0
  26. package/dist/clients/feed/feed.js +572 -0
  27. package/dist/clients/feed/index.d.ts +15 -0
  28. package/dist/clients/feed/index.d.ts.map +1 -0
  29. package/dist/clients/feed/index.js +34 -0
  30. package/dist/clients/feed/interfaces.d.ts +60 -0
  31. package/dist/clients/feed/interfaces.d.ts.map +1 -0
  32. package/dist/clients/feed/interfaces.js +2 -0
  33. package/dist/clients/feed/store.d.ts +3 -0
  34. package/dist/clients/feed/store.d.ts.map +1 -0
  35. package/dist/clients/feed/store.js +72 -0
  36. package/dist/clients/feed/types.d.ts +34 -0
  37. package/dist/clients/feed/types.d.ts.map +1 -0
  38. package/dist/clients/feed/types.js +2 -0
  39. package/dist/clients/feed/utils.d.ts +4 -0
  40. package/dist/clients/feed/utils.d.ts.map +1 -0
  41. package/dist/clients/feed/utils.js +21 -0
  42. package/dist/clients/preferences/index.d.ts +46 -0
  43. package/dist/clients/preferences/index.d.ts.map +1 -0
  44. package/dist/clients/preferences/index.js +129 -0
  45. package/dist/clients/preferences/interfaces.d.ts +26 -0
  46. package/dist/clients/preferences/interfaces.d.ts.map +1 -0
  47. package/dist/clients/preferences/interfaces.js +2 -0
  48. package/dist/clients/users/index.d.ts +16 -0
  49. package/dist/clients/users/index.d.ts.map +1 -0
  50. package/dist/clients/users/index.js +56 -0
  51. package/dist/clients/users/interfaces.d.ts +8 -0
  52. package/dist/clients/users/interfaces.d.ts.map +1 -0
  53. package/dist/clients/users/interfaces.js +2 -0
  54. package/dist/esm/api.js +44 -84
  55. package/dist/esm/api.js.map +1 -1
  56. package/dist/esm/clients/feed/feed.js +296 -603
  57. package/dist/esm/clients/feed/feed.js.map +1 -1
  58. package/dist/esm/clients/feed/index.js +28 -12
  59. package/dist/esm/clients/feed/index.js.map +1 -1
  60. package/dist/esm/clients/feed/store.js +37 -71
  61. package/dist/esm/clients/feed/store.js.map +1 -1
  62. package/dist/esm/clients/feed/utils.js +10 -15
  63. package/dist/esm/clients/feed/utils.js.map +1 -1
  64. package/dist/esm/clients/preferences/index.js +79 -146
  65. package/dist/esm/clients/preferences/index.js.map +1 -1
  66. package/dist/esm/clients/users/index.js +52 -76
  67. package/dist/esm/clients/users/index.js.map +1 -1
  68. package/dist/esm/index.js +12 -11
  69. package/dist/esm/index.js.map +1 -1
  70. package/dist/esm/knock.js +72 -51
  71. package/dist/esm/knock.js.map +1 -1
  72. package/dist/esm/networkStatus.js +14 -10
  73. package/dist/esm/networkStatus.js.map +1 -1
  74. package/dist/index.d.ts +11 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +43 -0
  77. package/dist/interfaces.d.ts +41 -0
  78. package/dist/interfaces.d.ts.map +1 -0
  79. package/dist/interfaces.js +2 -0
  80. package/dist/knock.d.ts +30 -0
  81. package/dist/knock.d.ts.map +1 -0
  82. package/dist/knock.js +135 -0
  83. package/dist/networkStatus.d.ts +8 -0
  84. package/dist/networkStatus.d.ts.map +1 -0
  85. package/dist/networkStatus.js +18 -0
  86. package/dist/types/api.d.ts +0 -2
  87. package/dist/types/api.d.ts.map +1 -1
  88. package/dist/types/clients/feed/feed.d.ts +12 -4
  89. package/dist/types/clients/feed/feed.d.ts.map +1 -1
  90. package/dist/types/clients/feed/index.d.ts +4 -0
  91. package/dist/types/clients/feed/index.d.ts.map +1 -1
  92. package/dist/types/clients/feed/store.d.ts.map +1 -1
  93. package/dist/types/clients/feed/types.d.ts +0 -1
  94. package/dist/types/clients/feed/types.d.ts.map +1 -1
  95. package/dist/types/index.d.ts +1 -1
  96. package/dist/types/index.d.ts.map +1 -1
  97. package/dist/types/interfaces.d.ts +7 -0
  98. package/dist/types/interfaces.d.ts.map +1 -1
  99. package/dist/types/knock.d.ts +12 -4
  100. package/dist/types/knock.d.ts.map +1 -1
  101. package/package.json +12 -8
  102. package/dist/cjs/clients/feed/interfaces.js +0 -6
  103. package/dist/cjs/clients/feed/interfaces.js.map +0 -1
  104. package/dist/cjs/clients/feed/types.js +0 -6
  105. package/dist/cjs/clients/feed/types.js.map +0 -1
  106. package/dist/cjs/clients/preferences/interfaces.js +0 -6
  107. package/dist/cjs/clients/preferences/interfaces.js.map +0 -1
  108. package/dist/cjs/clients/users/interfaces.js +0 -6
  109. package/dist/cjs/clients/users/interfaces.js.map +0 -1
  110. package/dist/cjs/interfaces.js +0 -6
  111. package/dist/cjs/interfaces.js.map +0 -1
  112. package/dist/esm/clients/feed/interfaces.js +0 -2
  113. package/dist/esm/clients/feed/interfaces.js.map +0 -1
  114. package/dist/esm/clients/feed/types.js +0 -2
  115. package/dist/esm/clients/feed/types.js.map +0 -1
  116. package/dist/esm/clients/preferences/interfaces.js +0 -2
  117. package/dist/esm/clients/preferences/interfaces.js.map +0 -1
  118. package/dist/esm/clients/users/interfaces.js +0 -2
  119. package/dist/esm/clients/users/interfaces.js.map +0 -1
  120. package/dist/esm/interfaces.js +0 -2
  121. package/dist/esm/interfaces.js.map +0 -1
@@ -1,939 +1,2 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports["default"] = void 0;
8
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
- var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
- var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
12
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
- var _eventemitter = require("eventemitter2");
14
- var _store = _interopRequireDefault(require("./store"));
15
- var _networkStatus = require("../../networkStatus");
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; }
18
- // Default options to apply
19
- var feedClientDefaults = {
20
- archived: "exclude"
21
- };
22
- var DEFAULT_DISCONNECT_DELAY = 2000;
23
- var Feed = /*#__PURE__*/function () {
24
- function Feed(knock, feedId, options) {
25
- var _this = this;
26
- (0, _classCallCheck2["default"])(this, Feed);
27
- this.knock = knock;
28
- this.feedId = feedId;
29
- (0, _defineProperty2["default"])(this, "apiClient", void 0);
30
- (0, _defineProperty2["default"])(this, "userFeedId", void 0);
31
- (0, _defineProperty2["default"])(this, "channel", void 0);
32
- (0, _defineProperty2["default"])(this, "broadcaster", void 0);
33
- (0, _defineProperty2["default"])(this, "defaultOptions", void 0);
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
37
- (0, _defineProperty2["default"])(this, "store", void 0);
38
- this.apiClient = knock.client();
39
- this.feedId = feedId;
40
- this.userFeedId = this.buildUserFeedId();
41
- this.store = (0, _store["default"])();
42
- this.broadcaster = new _eventemitter.EventEmitter2({
43
- wildcard: true,
44
- delimiter: "."
45
- });
46
- this.defaultOptions = _objectSpread(_objectSpread({}, feedClientDefaults), options);
47
-
48
- // In server environments we might not have a socket connection
49
- if (this.apiClient.socket) {
50
- this.channel = this.apiClient.socket.channel("feeds:".concat(this.userFeedId), this.defaultOptions);
51
- this.channel.on("new-message", function (resp) {
52
- return _this.onNewMessageReceived(resp);
53
- });
54
- }
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)
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
- }
62
- }
63
-
64
- /**
65
- * Cleans up a feed instance by destroying the store and disconnecting
66
- * an open socket connection.
67
- */
68
- (0, _createClass2["default"])(Feed, [{
69
- key: "teardown",
70
- value: function teardown() {
71
- if (this.channel) {
72
- this.channel.leave();
73
- this.channel.off("new-message");
74
- }
75
- this.broadcaster.removeAllListeners();
76
- this.store.destroy();
77
- if (this.broadcastChannel) {
78
- this.broadcastChannel.close();
79
- }
80
- }
81
-
82
- /*
83
- Initializes a real-time connection to Knock, connecting the websocket for the
84
- current ApiClient instance if the socket is not already connected.
85
- */
86
- }, {
87
- key: "listenForUpdates",
88
- value: function listenForUpdates() {
89
- var _this2 = this;
90
- // Connect the socket only if we don't already have a connection
91
- if (this.apiClient.socket && !this.apiClient.socket.isConnected()) {
92
- this.apiClient.socket.connect();
93
- }
94
-
95
- // Only join the channel if we're not already in a joining state
96
- if (this.channel && ["closed", "errored"].includes(this.channel.state)) {
97
- this.channel.join();
98
- }
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)
102
- if (this.broadcastChannel && this.defaultOptions.__experimentalCrossBrowserUpdates === true) {
103
- this.broadcastChannel.onmessage = function (e) {
104
- switch (e.data.type) {
105
- case "items:archived":
106
- case "items:unarchived":
107
- case "items:seen":
108
- case "items:unseen":
109
- case "items:read":
110
- case "items:unread":
111
- case "items:all_read":
112
- case "items:all_seen":
113
- case "items:all_archived":
114
- // When items are updated in any other tab, simply refetch to get the latest state
115
- // to make sure that the state gets updated accordingly. In the future here we could
116
- // maybe do this optimistically without the fetch.
117
- return _this2.fetch();
118
- break;
119
- default:
120
- return null;
121
- }
122
- };
123
- }
124
- }
125
-
126
- /* Binds a handler to be invoked when event occurs */
127
- }, {
128
- key: "on",
129
- value: function on(eventName, callback) {
130
- this.broadcaster.on(eventName, callback);
131
- }
132
- }, {
133
- key: "off",
134
- value: function off(eventName, callback) {
135
- this.broadcaster.off(eventName, callback);
136
- }
137
- }, {
138
- key: "getState",
139
- value: function getState() {
140
- return this.store.getState();
141
- }
142
- }, {
143
- key: "markAsSeen",
144
- value: function () {
145
- var _markAsSeen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(itemOrItems) {
146
- var now;
147
- return _regenerator["default"].wrap(function _callee$(_context) {
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();
158
- }
159
- }, _callee, this);
160
- }));
161
- function markAsSeen(_x) {
162
- return _markAsSeen.apply(this, arguments);
163
- }
164
- return markAsSeen;
165
- }()
166
- }, {
167
- key: "markAllAsSeen",
168
- value: function () {
169
- var _markAllAsSeen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2() {
170
- var _this$store, getState, setState, _getState, metadata, items, isViewingOnlyUnseen, attrs, itemIds, result;
171
- return _regenerator["default"].wrap(function _callee2$(_context2) {
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
- }));
203
- });
204
- attrs = {
205
- seen_at: new Date().toISOString()
206
- };
207
- itemIds = items.map(function (item) {
208
- return item.id;
209
- });
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();
230
- }
231
- }, _callee2, this);
232
- }));
233
- function markAllAsSeen() {
234
- return _markAllAsSeen.apply(this, arguments);
235
- }
236
- return markAllAsSeen;
237
- }()
238
- }, {
239
- key: "markAsUnseen",
240
- value: function () {
241
- var _markAsUnseen = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(itemOrItems) {
242
- return _regenerator["default"].wrap(function _callee3$(_context3) {
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();
252
- }
253
- }, _callee3, this);
254
- }));
255
- function markAsUnseen(_x2) {
256
- return _markAsUnseen.apply(this, arguments);
257
- }
258
- return markAsUnseen;
259
- }()
260
- }, {
261
- key: "markAsRead",
262
- value: function () {
263
- var _markAsRead = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(itemOrItems) {
264
- var now;
265
- return _regenerator["default"].wrap(function _callee4$(_context4) {
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();
276
- }
277
- }, _callee4, this);
278
- }));
279
- function markAsRead(_x3) {
280
- return _markAsRead.apply(this, arguments);
281
- }
282
- return markAsRead;
283
- }()
284
- }, {
285
- key: "markAllAsRead",
286
- value: function () {
287
- var _markAllAsRead = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5() {
288
- var _this$store2, getState, setState, _getState2, metadata, items, isViewingOnlyUnread, attrs, itemIds, result;
289
- return _regenerator["default"].wrap(function _callee5$(_context5) {
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
- }));
314
- });
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
- }));
321
- });
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();
348
- }
349
- }, _callee5, this);
350
- }));
351
- function markAllAsRead() {
352
- return _markAllAsRead.apply(this, arguments);
353
- }
354
- return markAllAsRead;
355
- }()
356
- }, {
357
- key: "markAsUnread",
358
- value: function () {
359
- var _markAsUnread = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6(itemOrItems) {
360
- return _regenerator["default"].wrap(function _callee6$(_context6) {
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();
370
- }
371
- }, _callee6, this);
372
- }));
373
- function markAsUnread(_x4) {
374
- return _markAsUnread.apply(this, arguments);
375
- }
376
- return markAsUnread;
377
- }()
378
- }, {
379
- key: "markAsInteracted",
380
- value: function () {
381
- var _markAsInteracted = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee7(itemOrItems) {
382
- var now;
383
- return _regenerator["default"].wrap(function _callee7$(_context7) {
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();
395
- }
396
- }, _callee7, this);
397
- }));
398
- function markAsInteracted(_x5) {
399
- return _markAsInteracted.apply(this, arguments);
400
- }
401
- return markAsInteracted;
402
- }()
403
- /*
404
- Marking one or more items as archived should:
405
- - Decrement the badge count for any unread / unseen items
406
- - Remove the item from the feed list when the `archived` flag is "exclude" (default)
407
- TODO: how do we handle rollbacks?
408
- */
409
- }, {
410
- key: "markAsArchived",
411
- value: (function () {
412
- var _markAsArchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee8(itemOrItems) {
413
- var _this$store3, getState, setState, state, shouldOptimisticallyRemoveItems, normalizedItems, itemIds, unseenCount, unreadCount, updatedMetadata, entriesToSet;
414
- return _regenerator["default"].wrap(function _callee8$(_context8) {
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);
458
- });
459
- setState(function (state) {
460
- return state.setResult({
461
- entries: entriesToSet,
462
- meta: updatedMetadata,
463
- page_info: state.pageInfo
464
- });
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();
476
- }
477
- }, _callee8, this);
478
- }));
479
- function markAsArchived(_x6) {
480
- return _markAsArchived.apply(this, arguments);
481
- }
482
- return markAsArchived;
483
- }())
484
- }, {
485
- key: "markAllAsArchived",
486
- value: function () {
487
- var _markAllAsArchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee9() {
488
- var _this$store4, setState, getState, _getState3, items, shouldOptimisticallyRemoveItems, result;
489
- return _regenerator["default"].wrap(function _callee9$(_context9) {
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;
509
- });
510
- store.setItemAttrs(itemIds, {
511
- archived_at: new Date().toISOString()
512
- });
513
- });
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();
531
- }
532
- }, _callee9, this);
533
- }));
534
- function markAllAsArchived() {
535
- return _markAllAsArchived.apply(this, arguments);
536
- }
537
- return markAllAsArchived;
538
- }()
539
- }, {
540
- key: "markAsUnarchived",
541
- value: function () {
542
- var _markAsUnarchived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee10(itemOrItems) {
543
- return _regenerator["default"].wrap(function _callee10$(_context10) {
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();
553
- }
554
- }, _callee10, this);
555
- }));
556
- function markAsUnarchived(_x7) {
557
- return _markAsUnarchived.apply(this, arguments);
558
- }
559
- return markAsUnarchived;
560
- }() /* Fetches the feed content, appending it to the store */
561
- }, {
562
- key: "fetch",
563
- value: (function () {
564
- var _fetch = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee11() {
565
- var options,
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;
579
- return _regenerator["default"].wrap(function _callee11$(_context11) {
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
- auto_manage_socket_connection: undefined,
604
- auto_manage_socket_connection_delay: undefined
605
- });
606
- _context11.next = 9;
607
- return this.apiClient.makeRequest({
608
- method: "GET",
609
- url: "/v1/users/".concat(this.knock.userId, "/feeds/").concat(this.feedId),
610
- params: queryParams
611
- });
612
- case 9:
613
- result = _context11.sent;
614
- if (!(result.statusCode === "error" || !result.body)) {
615
- _context11.next = 13;
616
- break;
617
- }
618
- setState(function (store) {
619
- return store.setNetworkStatus(_networkStatus.NetworkStatus.error);
620
- });
621
- return _context11.abrupt("return", {
622
- status: result.statusCode,
623
- data: result.error || result.body
624
- });
625
- case 13:
626
- response = {
627
- entries: result.body.entries,
628
- meta: result.body.meta,
629
- page_info: result.body.page_info
630
- };
631
- if (options.before) {
632
- opts = {
633
- shouldSetPage: false,
634
- shouldAppend: true
635
- };
636
- setState(function (state) {
637
- return state.setResult(response, opts);
638
- });
639
- } else if (options.after) {
640
- _opts = {
641
- shouldSetPage: true,
642
- shouldAppend: true
643
- };
644
- setState(function (state) {
645
- return state.setResult(response, _opts);
646
- });
647
- } else {
648
- setState(function (state) {
649
- return state.setResult(response);
650
- });
651
- }
652
-
653
- // Legacy `messages.new` event, should be removed in a future version
654
- this.broadcast("messages.new", response);
655
-
656
- // Broadcast the appropriate event type depending on the fetch source
657
- feedEventType = options.__fetchSource === "socket" ? "items.received.realtime" : "items.received.page";
658
- eventPayload = {
659
- items: response.entries,
660
- metadata: response.meta,
661
- event: feedEventType
662
- };
663
- this.broadcast(eventPayload.event, eventPayload);
664
- return _context11.abrupt("return", {
665
- data: response,
666
- status: result.statusCode
667
- });
668
- case 20:
669
- case "end":
670
- return _context11.stop();
671
- }
672
- }, _callee11, this);
673
- }));
674
- function fetch() {
675
- return _fetch.apply(this, arguments);
676
- }
677
- return fetch;
678
- }())
679
- }, {
680
- key: "fetchNextPage",
681
- value: function () {
682
- var _fetchNextPage = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee12() {
683
- var getState, _getState5, pageInfo;
684
- return _regenerator["default"].wrap(function _callee12$(_context12) {
685
- while (1) switch (_context12.prev = _context12.next) {
686
- case 0:
687
- // Attempts to fetch the next page of results (if we have any)
688
- getState = this.store.getState;
689
- _getState5 = getState(), pageInfo = _getState5.pageInfo;
690
- if (pageInfo.after) {
691
- _context12.next = 4;
692
- break;
693
- }
694
- return _context12.abrupt("return");
695
- case 4:
696
- this.fetch({
697
- after: pageInfo.after,
698
- __loadingType: _networkStatus.NetworkStatus.fetchMore
699
- });
700
- case 5:
701
- case "end":
702
- return _context12.stop();
703
- }
704
- }, _callee12, this);
705
- }));
706
- function fetchNextPage() {
707
- return _fetchNextPage.apply(this, arguments);
708
- }
709
- return fetchNextPage;
710
- }()
711
- }, {
712
- key: "broadcast",
713
- value: function broadcast(eventName, data) {
714
- this.broadcaster.emit(eventName, data);
715
- }
716
-
717
- // Invoked when a new real-time message comes in from the socket
718
- }, {
719
- key: "onNewMessageReceived",
720
- value: function () {
721
- var _onNewMessageReceived = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee13(_ref) {
722
- var metadata, _this$store6, getState, setState, _getState6, items, currentHead;
723
- return _regenerator["default"].wrap(function _callee13$(_context13) {
724
- while (1) switch (_context13.prev = _context13.next) {
725
- case 0:
726
- metadata = _ref.metadata;
727
- // Handle the new message coming in
728
- _this$store6 = this.store, getState = _this$store6.getState, setState = _this$store6.setState;
729
- _getState6 = getState(), items = _getState6.items;
730
- currentHead = items[0]; // Optimistically set the badge counts
731
- setState(function (state) {
732
- return state.setMetadata(metadata);
733
- });
734
- // Fetch the items before the current head (if it exists)
735
- this.fetch({
736
- before: currentHead === null || currentHead === void 0 ? void 0 : currentHead.__cursor,
737
- __fetchSource: "socket"
738
- });
739
- case 6:
740
- case "end":
741
- return _context13.stop();
742
- }
743
- }, _callee13, this);
744
- }));
745
- function onNewMessageReceived(_x8) {
746
- return _onNewMessageReceived.apply(this, arguments);
747
- }
748
- return onNewMessageReceived;
749
- }()
750
- }, {
751
- key: "buildUserFeedId",
752
- value: function buildUserFeedId() {
753
- return "".concat(this.feedId, ":").concat(this.knock.userId);
754
- }
755
- }, {
756
- key: "optimisticallyPerformStatusUpdate",
757
- value: function optimisticallyPerformStatusUpdate(itemOrItems, type, attrs, badgeCountAttr) {
758
- var _this$store7 = this.store,
759
- getState = _this$store7.getState,
760
- setState = _this$store7.setState;
761
- var normalizedItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
762
- var itemIds = normalizedItems.map(function (item) {
763
- return item.id;
764
- });
765
- if (badgeCountAttr) {
766
- var _getState7 = getState(),
767
- metadata = _getState7.metadata;
768
-
769
- // We only want to update the counts of items that have not already been counted towards the
770
- // badge count total to avoid updating the badge count unnecessarily.
771
- var itemsToUpdate = normalizedItems.filter(function (item) {
772
- switch (type) {
773
- case "seen":
774
- return item.seen_at === null;
775
- case "unseen":
776
- return item.seen_at !== null;
777
- case "read":
778
- case "interacted":
779
- return item.read_at === null;
780
- case "unread":
781
- return item.read_at !== null;
782
- default:
783
- return true;
784
- }
785
- });
786
-
787
- // Tnis is a hack to determine the direction of whether we're
788
- // adding or removing from the badge count
789
- var direction = type.startsWith("un") ? itemsToUpdate.length : -itemsToUpdate.length;
790
- setState(function (store) {
791
- return store.setMetadata(_objectSpread(_objectSpread({}, metadata), {}, (0, _defineProperty2["default"])({}, badgeCountAttr, Math.max(0, metadata[badgeCountAttr] + direction))));
792
- });
793
- }
794
-
795
- // Update the items with the given attributes
796
- setState(function (store) {
797
- return store.setItemAttrs(itemIds, attrs);
798
- });
799
- }
800
- }, {
801
- key: "makeStatusUpdate",
802
- value: function () {
803
- var _makeStatusUpdate = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee14(itemOrItems, type) {
804
- var items, itemIds, result;
805
- return _regenerator["default"].wrap(function _callee14$(_context14) {
806
- while (1) switch (_context14.prev = _context14.next) {
807
- case 0:
808
- // Always treat items as a batch to use the corresponding batch endpoint
809
- items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
810
- itemIds = items.map(function (item) {
811
- return item.id;
812
- });
813
- _context14.next = 4;
814
- return this.apiClient.makeRequest({
815
- method: "POST",
816
- url: "/v1/messages/batch/".concat(type),
817
- data: {
818
- message_ids: itemIds
819
- }
820
- });
821
- case 4:
822
- result = _context14.sent;
823
- // Emit the event that these items had their statuses changed
824
- // Note: we do this after the update to ensure that the server event actually completed
825
- this.broadcaster.emit("items:".concat(type), {
826
- items: items
827
- });
828
- this.broadcastOverChannel("items:".concat(type), {
829
- items: items
830
- });
831
- return _context14.abrupt("return", result);
832
- case 8:
833
- case "end":
834
- return _context14.stop();
835
- }
836
- }, _callee14, this);
837
- }));
838
- function makeStatusUpdate(_x9, _x10) {
839
- return _makeStatusUpdate.apply(this, arguments);
840
- }
841
- return makeStatusUpdate;
842
- }()
843
- }, {
844
- key: "makeBulkStatusUpdate",
845
- value: function () {
846
- var _makeBulkStatusUpdate = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee15(type) {
847
- var options;
848
- return _regenerator["default"].wrap(function _callee15$(_context15) {
849
- while (1) switch (_context15.prev = _context15.next) {
850
- case 0:
851
- // The base scope for the call should take into account all of the options currently
852
- // set on the feed, as well as being scoped for the current user. We do this so that
853
- // we ONLY make changes to the messages that are currently in view on this feed, and not
854
- // all messages that exist.
855
- options = {
856
- user_ids: [this.knock.userId],
857
- engagement_status: this.defaultOptions.status !== "all" ? this.defaultOptions.status : undefined,
858
- archived: this.defaultOptions.archived,
859
- has_tenant: this.defaultOptions.has_tenant,
860
- tenants: this.defaultOptions.tenant ? [this.defaultOptions.tenant] : undefined
861
- };
862
- _context15.next = 3;
863
- return this.apiClient.makeRequest({
864
- method: "POST",
865
- url: "/v1/channels/".concat(this.feedId, "/messages/bulk/").concat(type),
866
- data: options
867
- });
868
- case 3:
869
- return _context15.abrupt("return", _context15.sent);
870
- case 4:
871
- case "end":
872
- return _context15.stop();
873
- }
874
- }, _callee15, this);
875
- }));
876
- function makeBulkStatusUpdate(_x11) {
877
- return _makeBulkStatusUpdate.apply(this, arguments);
878
- }
879
- return makeBulkStatusUpdate;
880
- }()
881
- }, {
882
- key: "broadcastOverChannel",
883
- value: function broadcastOverChannel(type, payload) {
884
- // The broadcastChannel may not be available in non-browser environments
885
- if (!this.broadcastChannel) {
886
- return;
887
- }
888
-
889
- // Here we stringify our payload and try and send as JSON such that we
890
- // don't get any `An object could not be cloned` errors when trying to broadcast
891
- try {
892
- var stringifiedPayload = JSON.parse(JSON.stringify(payload));
893
- this.broadcastChannel.postMessage({
894
- type: type,
895
- payload: stringifiedPayload
896
- });
897
- } catch (e) {
898
- console.warn("Could not broadcast ".concat(type, ", got error: ").concat(e));
899
- }
900
- }
901
-
902
- /**
903
- * Listen for changes to document visibility and automatically disconnect
904
- * or reconnect the socket after a delay
905
- */
906
- }, {
907
- key: "setupAutoSocketManager",
908
- value: function setupAutoSocketManager(autoManageSocketConnectionDelay) {
909
- var _this3 = this;
910
- var disconnectDelay = autoManageSocketConnectionDelay !== null && autoManageSocketConnectionDelay !== void 0 ? autoManageSocketConnectionDelay : DEFAULT_DISCONNECT_DELAY;
911
- document.addEventListener("visibilitychange", function () {
912
- if (document.visibilityState === "hidden") {
913
- // When the tab is hidden, clean up the socket connection after a delay
914
- _this3.disconnectTimer = setTimeout(function () {
915
- _this3.apiClient.disconnectSocket();
916
- _this3.disconnectTimer = null;
917
- }, disconnectDelay);
918
- } else if (document.visibilityState === "visible") {
919
- var _this3$apiClient$sock;
920
- // When the tab is visible, clear the disconnect timer if active to cancel disconnecting
921
- // This handles cases where the tab is only briefly hidden to avoid unnecessary disconnects
922
- if (_this3.disconnectTimer) {
923
- clearTimeout(_this3.disconnectTimer);
924
- _this3.disconnectTimer = null;
925
- }
926
-
927
- // If the socket is not connected, try to reconnect
928
- if (!((_this3$apiClient$sock = _this3.apiClient.socket) !== null && _this3$apiClient$sock !== void 0 && _this3$apiClient$sock.isConnected())) {
929
- _this3.apiClient.reconnectSocket();
930
- _this3.fetch();
931
- }
932
- }
933
- });
934
- }
935
- }]);
936
- return Feed;
937
- }();
938
- var _default = exports["default"] = Feed;
939
- //# sourceMappingURL=feed.js.map
1
+ "use strict";var _=Object.defineProperty;var k=(m,e,t)=>e in m?_(m,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):m[e]=t;var u=(m,e,t)=>(k(m,typeof e!="symbol"?e+"":e,t),t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const p=require("eventemitter2"),f=require("../../networkStatus.js"),S=require("./store.js"),g={archived:"exclude"},v=2e3;class y{constructor(e,t,s){u(this,"userFeedId");u(this,"channel");u(this,"broadcaster");u(this,"defaultOptions");u(this,"broadcastChannel");u(this,"disconnectTimer",null);u(this,"hasSubscribedToRealTimeUpdates",!1);u(this,"store");this.knock=e,this.feedId=t,this.feedId=t,this.userFeedId=this.buildUserFeedId(),this.store=S.default(),this.broadcaster=new p.EventEmitter2({wildcard:!0,delimiter:"."}),this.defaultOptions={...g,...s},this.knock.log(`[Feed] Initialized a feed on channel ${t}`),this.initializeRealtimeConnection(),this.setupBroadcastChannel()}reinitialize(){this.userFeedId=this.buildUserFeedId(),this.initializeRealtimeConnection(),this.setupBroadcastChannel()}teardown(){this.knock.log("[Feed] Tearing down feed instance"),this.channel&&(this.channel.leave(),this.channel.off("new-message")),this.disconnectTimer&&(clearTimeout(this.disconnectTimer),this.disconnectTimer=null),this.broadcastChannel&&this.broadcastChannel.close()}dispose(){this.knock.log("[Feed] Disposing of feed instance"),this.teardown(),this.broadcaster.removeAllListeners(),this.knock.feeds.removeInstance(this)}listenForUpdates(){this.knock.log("[Feed] Connecting to real-time service"),this.hasSubscribedToRealTimeUpdates=!0;const e=this.knock.client().socket;e&&!e.isConnected()&&e.connect(),this.channel&&["closed","errored"].includes(this.channel.state)&&this.channel.join()}on(e,t){this.broadcaster.on(e,t)}off(e,t){this.broadcaster.off(e,t)}getState(){return this.store.getState()}async markAsSeen(e){const t=new Date().toISOString();return this.optimisticallyPerformStatusUpdate(e,"seen",{seen_at:t},"unseen_count"),this.makeStatusUpdate(e,"seen")}async markAllAsSeen(){const{getState:e,setState:t}=this.store,{metadata:s,items:a}=e();if(this.defaultOptions.status==="unseen")t(i=>i.resetStore({...s,total_count:0,unseen_count:0}));else{t(o=>o.setMetadata({...s,unseen_count:0}));const i={seen_at:new Date().toISOString()},l=a.map(o=>o.id);t(o=>o.setItemAttrs(l,i))}const n=await this.makeBulkStatusUpdate("seen");return this.broadcaster.emit("items:all_seen",{items:a}),this.broadcastOverChannel("items:all_seen",{items:a}),n}async markAsUnseen(e){return this.optimisticallyPerformStatusUpdate(e,"unseen",{seen_at:null},"unseen_count"),this.makeStatusUpdate(e,"unseen")}async markAsRead(e){const t=new Date().toISOString();return this.optimisticallyPerformStatusUpdate(e,"read",{read_at:t},"unread_count"),this.makeStatusUpdate(e,"read")}async markAllAsRead(){const{getState:e,setState:t}=this.store,{metadata:s,items:a}=e();if(this.defaultOptions.status==="unread")t(i=>i.resetStore({...s,total_count:0,unread_count:0}));else{t(o=>o.setMetadata({...s,unread_count:0}));const i={read_at:new Date().toISOString()},l=a.map(o=>o.id);t(o=>o.setItemAttrs(l,i))}const n=await this.makeBulkStatusUpdate("read");return this.broadcaster.emit("items:all_read",{items:a}),this.broadcastOverChannel("items:all_read",{items:a}),n}async markAsUnread(e){return this.optimisticallyPerformStatusUpdate(e,"unread",{read_at:null},"unread_count"),this.makeStatusUpdate(e,"unread")}async markAsInteracted(e){const t=new Date().toISOString();return this.optimisticallyPerformStatusUpdate(e,"interacted",{read_at:t,interacted_at:t},"unread_count"),this.makeStatusUpdate(e,"interacted")}async markAsArchived(e){const{getState:t,setState:s}=this.store,a=t(),r=this.defaultOptions.archived==="exclude",n=Array.isArray(e)?e:[e],i=n.map(l=>l.id);if(r){const l=n.filter(c=>!c.seen_at).length,o=n.filter(c=>!c.read_at).length,d={...a.metadata,total_count:a.metadata.total_count-n.length,unseen_count:a.metadata.unseen_count-l,unread_count:a.metadata.unread_count-o},h=a.items.filter(c=>!i.includes(c.id));s(c=>c.setResult({entries:h,meta:d,page_info:c.pageInfo}))}else a.setItemAttrs(i,{archived_at:new Date().toISOString()});return this.makeStatusUpdate(e,"archived")}async markAllAsArchived(){const{setState:e,getState:t}=this.store,{items:s}=t(),a=this.defaultOptions.archived==="exclude";e(a?n=>n.resetStore():n=>{const i=s.map(l=>l.id);n.setItemAttrs(i,{archived_at:new Date().toISOString()})});const r=await this.makeBulkStatusUpdate("archive");return this.broadcaster.emit("items:all_archived",{items:s}),this.broadcastOverChannel("items:all_archived",{items:s}),r}async markAsUnarchived(e){return this.optimisticallyPerformStatusUpdate(e,"unarchived",{archived_at:null}),this.makeStatusUpdate(e,"unarchived")}async fetch(e={}){const{setState:t,getState:s}=this.store,{networkStatus:a}=s();if(f.isRequestInFlight(a))return;t(d=>d.setNetworkStatus(e.__loadingType??f.NetworkStatus.loading));const r={...this.defaultOptions,...e,__loadingType:void 0,__fetchSource:void 0,__experimentalCrossBrowserUpdates:void 0,auto_manage_socket_connection:void 0,auto_manage_socket_connection_delay:void 0},n=await this.knock.client().makeRequest({method:"GET",url:`/v1/users/${this.knock.userId}/feeds/${this.feedId}`,params:r});if(n.statusCode==="error"||!n.body)return t(d=>d.setNetworkStatus(f.NetworkStatus.error)),{status:n.statusCode,data:n.error||n.body};const i={entries:n.body.entries,meta:n.body.meta,page_info:n.body.page_info};if(e.before){const d={shouldSetPage:!1,shouldAppend:!0};t(h=>h.setResult(i,d))}else if(e.after){const d={shouldSetPage:!0,shouldAppend:!0};t(h=>h.setResult(i,d))}else t(d=>d.setResult(i));this.broadcast("messages.new",i);const l=e.__fetchSource==="socket"?"items.received.realtime":"items.received.page",o={items:i.entries,metadata:i.meta,event:l};return this.broadcast(o.event,o),{data:i,status:n.statusCode}}async fetchNextPage(){const{getState:e}=this.store,{pageInfo:t}=e();t.after&&this.fetch({after:t.after,__loadingType:f.NetworkStatus.fetchMore})}broadcast(e,t){this.broadcaster.emit(e,t)}async onNewMessageReceived({metadata:e}){this.knock.log("[Feed] Received new real-time message");const{getState:t,setState:s}=this.store,{items:a}=t(),r=a[0];s(n=>n.setMetadata(e)),this.fetch({before:r==null?void 0:r.__cursor,__fetchSource:"socket"})}buildUserFeedId(){return`${this.feedId}:${this.knock.userId}`}optimisticallyPerformStatusUpdate(e,t,s,a){const{getState:r,setState:n}=this.store,i=Array.isArray(e)?e:[e],l=i.map(o=>o.id);if(a){const{metadata:o}=r(),d=i.filter(c=>{switch(t){case"seen":return c.seen_at===null;case"unseen":return c.seen_at!==null;case"read":case"interacted":return c.read_at===null;case"unread":return c.read_at!==null;default:return!0}}),h=t.startsWith("un")?d.length:-d.length;n(c=>c.setMetadata({...o,[a]:Math.max(0,o[a]+h)}))}n(o=>o.setItemAttrs(l,s))}async makeStatusUpdate(e,t){const s=Array.isArray(e)?e:[e],a=s.map(n=>n.id),r=await this.knock.client().makeRequest({method:"POST",url:`/v1/messages/batch/${t}`,data:{message_ids:a}});return this.broadcaster.emit(`items:${t}`,{items:s}),this.broadcastOverChannel(`items:${t}`,{items:s}),r}async makeBulkStatusUpdate(e){const t={user_ids:[this.knock.userId],engagement_status:this.defaultOptions.status!=="all"?this.defaultOptions.status:void 0,archived:this.defaultOptions.archived,has_tenant:this.defaultOptions.has_tenant,tenants:this.defaultOptions.tenant?[this.defaultOptions.tenant]:void 0};return await this.knock.client().makeRequest({method:"POST",url:`/v1/channels/${this.feedId}/messages/bulk/${e}`,data:t})}setupBroadcastChannel(){this.broadcastChannel=typeof self<"u"&&"BroadcastChannel"in self?new BroadcastChannel(`knock:feed:${this.userFeedId}`):null,this.broadcastChannel&&this.defaultOptions.__experimentalCrossBrowserUpdates===!0&&(this.broadcastChannel.onmessage=e=>{switch(e.data.type){case"items:archived":case"items:unarchived":case"items:seen":case"items:unseen":case"items:read":case"items:unread":case"items:all_read":case"items:all_seen":case"items:all_archived":return this.fetch();default:return null}})}broadcastOverChannel(e,t){if(this.broadcastChannel)try{const s=JSON.parse(JSON.stringify(t));this.broadcastChannel.postMessage({type:e,payload:s})}catch(s){console.warn(`Could not broadcast ${e}, got error: ${s}`)}}initializeRealtimeConnection(){const{socket:e}=this.knock.client();e&&(this.channel=e.channel(`feeds:${this.userFeedId}`,this.defaultOptions),this.channel.on("new-message",t=>this.onNewMessageReceived(t)),this.defaultOptions.auto_manage_socket_connection&&this.setupAutoSocketManager(this.defaultOptions.auto_manage_socket_connection_delay),this.hasSubscribedToRealTimeUpdates&&(e.isConnected()||e.connect(),this.channel.join()))}setupAutoSocketManager(e){const t=e??v;document.addEventListener("visibilitychange",()=>{var a;const s=this.knock.client();document.visibilityState==="hidden"?this.disconnectTimer=setTimeout(()=>{var r;(r=s.socket)==null||r.disconnect(),this.disconnectTimer=null},t):document.visibilityState==="visible"&&(this.disconnectTimer&&(clearTimeout(this.disconnectTimer),this.disconnectTimer=null),(a=s.socket)!=null&&a.isConnected()||this.initializeRealtimeConnection())})}}exports.default=y;
2
+ //# sourceMappingURL=feed.js.map