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