@webex/internal-plugin-conversation 2.59.2 → 2.59.3-next.1
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/.eslintrc.js +6 -6
- package/README.md +47 -47
- package/babel.config.js +3 -3
- package/dist/activities.js +4 -4
- package/dist/activities.js.map +1 -1
- package/dist/activity-thread-ordering.js +34 -34
- package/dist/activity-thread-ordering.js.map +1 -1
- package/dist/config.js +12 -12
- package/dist/config.js.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/conversation.js +474 -474
- package/dist/conversation.js.map +1 -1
- package/dist/convo-error.js +4 -4
- package/dist/convo-error.js.map +1 -1
- package/dist/decryption-transforms.js +155 -155
- package/dist/decryption-transforms.js.map +1 -1
- package/dist/encryption-transforms.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/share-activity.js +57 -57
- package/dist/share-activity.js.map +1 -1
- package/dist/to-array.js +7 -7
- package/dist/to-array.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +21 -20
- package/process +1 -1
- package/src/activities.js +157 -157
- package/src/activity-thread-ordering.js +283 -283
- package/src/activity-threading.md +282 -282
- package/src/config.js +37 -37
- package/src/constants.js +3 -3
- package/src/conversation.js +2535 -2535
- package/src/convo-error.js +15 -15
- package/src/decryption-transforms.js +541 -541
- package/src/encryption-transforms.js +345 -345
- package/src/index.js +327 -327
- package/src/share-activity.js +436 -436
- package/src/to-array.js +29 -29
- package/test/integration/spec/create.js +290 -290
- package/test/integration/spec/encryption.js +333 -333
- package/test/integration/spec/get.js +1255 -1255
- package/test/integration/spec/mercury.js +94 -94
- package/test/integration/spec/share.js +537 -537
- package/test/integration/spec/verbs.js +1041 -1041
- package/test/unit/spec/conversation.js +823 -823
- package/test/unit/spec/decrypt-transforms.js +460 -460
- package/test/unit/spec/encryption-transforms.js +93 -93
- package/test/unit/spec/share-activity.js +178 -178
|
@@ -1,283 +1,283 @@
|
|
|
1
|
-
import {last} from 'lodash';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
ACTIVITY_TYPES,
|
|
5
|
-
getParentId,
|
|
6
|
-
isNewer,
|
|
7
|
-
getActivityType,
|
|
8
|
-
sortActivitiesByPublishedDate,
|
|
9
|
-
NEWER,
|
|
10
|
-
OLDER,
|
|
11
|
-
INITIAL,
|
|
12
|
-
MID,
|
|
13
|
-
} from './activities';
|
|
14
|
-
|
|
15
|
-
export const defaultMinDisplayableActivities = 20;
|
|
16
|
-
export const minBatchSize = 10;
|
|
17
|
-
export const fetchLoopCountMax = 100;
|
|
18
|
-
export const batchSizeIncrementCount = 10;
|
|
19
|
-
|
|
20
|
-
// use accessors for ease of refactoring underlying implementation
|
|
21
|
-
/**
|
|
22
|
-
* @param {Map} destination destination map object for data. Currently a Map, but could be refactored to use any type.
|
|
23
|
-
* @param {string} key
|
|
24
|
-
* @param {any} value
|
|
25
|
-
* @returns {Map}
|
|
26
|
-
*/
|
|
27
|
-
export const setValue = (destination, key, value) => destination.set(key, value);
|
|
28
|
-
/**
|
|
29
|
-
* @param {Map} source source map object to access. Currently expects a Map, but could be refactored to use any type
|
|
30
|
-
* @param {string} key
|
|
31
|
-
* @returns {Map}
|
|
32
|
-
*/
|
|
33
|
-
export const getValue = (source, key) => source.get(key);
|
|
34
|
-
|
|
35
|
-
export const getActivityObjectsFromMap = (hashMap) =>
|
|
36
|
-
Array.from(hashMap).map(([, activity]) => activity);
|
|
37
|
-
/**
|
|
38
|
-
* creates maps for various activity types and defines handlers for working with stored activities
|
|
39
|
-
* utilizes revealing module pattern to close over state and only expose certain necessary functions for altering state
|
|
40
|
-
* @function
|
|
41
|
-
* @returns {object}
|
|
42
|
-
* getActivityHandlerByKey(activityType) - accepts a key to map to a defined activity handler
|
|
43
|
-
* getActivityByTypeAndParentId(activityType, parentId) accepts a key and a parent ID to return an activity of that type whose parent is the parentId
|
|
44
|
-
*/
|
|
45
|
-
export const activityManager = () => {
|
|
46
|
-
const replyActivityHash = new Map();
|
|
47
|
-
const editActivityHash = new Map();
|
|
48
|
-
const reactionActivityHash = new Map();
|
|
49
|
-
const reactionSelfActivityHash = new Map();
|
|
50
|
-
|
|
51
|
-
const handleNewReply = (replyAct) => {
|
|
52
|
-
const replyParentId = getParentId(replyAct);
|
|
53
|
-
const existingReplyHash = getValue(replyActivityHash, replyParentId);
|
|
54
|
-
|
|
55
|
-
if (existingReplyHash) {
|
|
56
|
-
setValue(existingReplyHash, replyAct.id, replyAct);
|
|
57
|
-
} else {
|
|
58
|
-
const replyHash = new Map();
|
|
59
|
-
|
|
60
|
-
setValue(replyHash, replyAct.id, replyAct);
|
|
61
|
-
setValue(replyActivityHash, replyParentId, replyHash);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const handleNewEdit = (editAct) => {
|
|
66
|
-
const isTombstone = editAct.verb === ACTIVITY_TYPES.TOMBSTONE;
|
|
67
|
-
|
|
68
|
-
// we can ignore tombstone edits in favor of the newer one
|
|
69
|
-
if (isTombstone) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const editParentId = getParentId(editAct);
|
|
74
|
-
const existingEdit = getValue(editActivityHash, editParentId);
|
|
75
|
-
|
|
76
|
-
// edited activity must be newer than what we already have
|
|
77
|
-
if (!existingEdit || isNewer(editAct, existingEdit)) {
|
|
78
|
-
setValue(editActivityHash, editParentId, editAct);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// logic is identical between reactions and reaction selfs, so handler simply passes the activity and the correct hash
|
|
83
|
-
const reactionHelper = (reactionAct, hash) => {
|
|
84
|
-
const reactionParentId = getParentId(reactionAct);
|
|
85
|
-
const existingReaction = getValue(hash, reactionParentId);
|
|
86
|
-
|
|
87
|
-
// reaction activity must be newer than what we already have
|
|
88
|
-
if (!existingReaction || isNewer(reactionAct, existingReaction)) {
|
|
89
|
-
setValue(hash, reactionParentId, reactionAct);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const handleNewReaction = (reactionAct) => {
|
|
94
|
-
reactionHelper(reactionAct, reactionActivityHash);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const handleNewReactionSelf = (reactionSelfAct) => {
|
|
98
|
-
reactionHelper(reactionSelfAct, reactionSelfActivityHash);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const getActivityHandlerByKey = (key) =>
|
|
102
|
-
({
|
|
103
|
-
[ACTIVITY_TYPES.REACTION]: handleNewReaction,
|
|
104
|
-
[ACTIVITY_TYPES.REACTION_SELF]: handleNewReactionSelf,
|
|
105
|
-
[ACTIVITY_TYPES.EDIT]: handleNewEdit,
|
|
106
|
-
[ACTIVITY_TYPES.REPLY]: handleNewReply,
|
|
107
|
-
}[key]);
|
|
108
|
-
|
|
109
|
-
const getActivityByTypeAndParentId = (type, id) =>
|
|
110
|
-
({
|
|
111
|
-
[ACTIVITY_TYPES.EDIT]: getValue(editActivityHash, id),
|
|
112
|
-
[ACTIVITY_TYPES.REPLY]: getValue(replyActivityHash, id),
|
|
113
|
-
[ACTIVITY_TYPES.REACTION]: getValue(reactionActivityHash, id),
|
|
114
|
-
[ACTIVITY_TYPES.REACTION_SELF]: getValue(reactionSelfActivityHash, id),
|
|
115
|
-
}[type]);
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
getActivityHandlerByKey,
|
|
119
|
-
getActivityByTypeAndParentId,
|
|
120
|
-
};
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* encapsulates state and logic for managing oldest and newest activities
|
|
125
|
-
* @returns {object} setters and getters for activity state management
|
|
126
|
-
*/
|
|
127
|
-
export const bookendManager = () => {
|
|
128
|
-
// keep track of generator state, like what our current oldest & newest activities are
|
|
129
|
-
let oldestAct;
|
|
130
|
-
let newestAct;
|
|
131
|
-
|
|
132
|
-
const getOldestAct = () => oldestAct;
|
|
133
|
-
const getNewestAct = () => newestAct;
|
|
134
|
-
|
|
135
|
-
const setOldestAct = (act) => {
|
|
136
|
-
if (!oldestAct) {
|
|
137
|
-
oldestAct = act;
|
|
138
|
-
} else if (isNewer(oldestAct, act)) {
|
|
139
|
-
oldestAct = act;
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const setNewestAct = (act) => {
|
|
144
|
-
if (!newestAct) {
|
|
145
|
-
newestAct = act;
|
|
146
|
-
} else if (isNewer(act, newestAct)) {
|
|
147
|
-
newestAct = act;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const setBookends = (activities) => {
|
|
152
|
-
const oldestActsFirst = sortActivitiesByPublishedDate(activities);
|
|
153
|
-
|
|
154
|
-
const newestInBatch = last(oldestActsFirst);
|
|
155
|
-
const oldestInBatch = oldestActsFirst[0];
|
|
156
|
-
|
|
157
|
-
setOldestAct(oldestInBatch);
|
|
158
|
-
setNewestAct(newestInBatch);
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
setBookends,
|
|
163
|
-
getNewestAct,
|
|
164
|
-
getOldestAct,
|
|
165
|
-
};
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* encapsulates state and logic for when there are no more fetchable activities from convo
|
|
170
|
-
* @returns {object} setters and getters for no more activities logic
|
|
171
|
-
*/
|
|
172
|
-
export const noMoreActivitiesManager = () => {
|
|
173
|
-
// used to determine if we should continue to fetch older activities
|
|
174
|
-
// must be set per iteration, as querying newer activities is still valid when all end of convo has been reached
|
|
175
|
-
let noMoreActs = false;
|
|
176
|
-
let noOlderActs = false;
|
|
177
|
-
let noNewerActs = false;
|
|
178
|
-
|
|
179
|
-
const getNoMoreActs = () => noMoreActs;
|
|
180
|
-
|
|
181
|
-
const checkAndSetNoOlderActs = (act) => {
|
|
182
|
-
if (!noOlderActs && getActivityType(act) === ACTIVITY_TYPES.CREATE) {
|
|
183
|
-
noOlderActs = true;
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const checkAndSetNoNewerActs = (activities) => {
|
|
188
|
-
if (!activities || !activities.length) {
|
|
189
|
-
noNewerActs = true;
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const checkAndSetNoMoreActs = (queryType, visibleActs, currentBatchSize) => {
|
|
194
|
-
if (
|
|
195
|
-
(queryType === NEWER && noNewerActs) ||
|
|
196
|
-
((queryType === OLDER || queryType === INITIAL) && noOlderActs) ||
|
|
197
|
-
(queryType === MID && visibleActs < currentBatchSize && noOlderActs)
|
|
198
|
-
) {
|
|
199
|
-
noMoreActs = true;
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
getNoMoreActs,
|
|
205
|
-
checkAndSetNoMoreActs,
|
|
206
|
-
checkAndSetNoNewerActs,
|
|
207
|
-
checkAndSetNoOlderActs,
|
|
208
|
-
};
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* encapsulates state and logic for managing root activities
|
|
213
|
-
* @returns {object} setters and getters for activity state management
|
|
214
|
-
*/
|
|
215
|
-
export const rootActivityManager = () => {
|
|
216
|
-
const rootActivityHash = new Map();
|
|
217
|
-
|
|
218
|
-
const addNewRoot = (rootAct) => {
|
|
219
|
-
setValue(rootActivityHash, rootAct.id, rootAct);
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const getRootActivityHash = () => rootActivityHash;
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
addNewRoot,
|
|
226
|
-
getRootActivityHash,
|
|
227
|
-
};
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
export const getLoopCounterFailsafe = () => {
|
|
231
|
-
let fetchLoopCount = 0;
|
|
232
|
-
|
|
233
|
-
return () => {
|
|
234
|
-
fetchLoopCount += 1;
|
|
235
|
-
if (fetchLoopCount > fetchLoopCountMax) {
|
|
236
|
-
throw new Error('max fetches reached');
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* creates activity query object
|
|
243
|
-
* @param {string} type type of query to create
|
|
244
|
-
* @param {object} queryOptions options to define query
|
|
245
|
-
* @param {string} [queryOptions.newestPublishedDate] the date of the newest fetched activity
|
|
246
|
-
* @param {string} [queryOptions.oldestPublishedDate] the date of the oldest fetched activity
|
|
247
|
-
* @param {number} [queryOptions.batchSize] the number of activities to query
|
|
248
|
-
* @param {object} [queryOptions.activityToSearch] a server activity to use to build middate query
|
|
249
|
-
* @returns {object}
|
|
250
|
-
*/
|
|
251
|
-
export const getQuery = (type, queryOptions) => {
|
|
252
|
-
const {newestPublishedDate, oldestPublishedDate, batchSize, activityToSearch = {}} = queryOptions;
|
|
253
|
-
|
|
254
|
-
switch (type) {
|
|
255
|
-
case NEWER: {
|
|
256
|
-
const sinceDate = newestPublishedDate + 1;
|
|
257
|
-
const lastActivityFirst = false;
|
|
258
|
-
|
|
259
|
-
return {sinceDate, lastActivityFirst};
|
|
260
|
-
}
|
|
261
|
-
case MID: {
|
|
262
|
-
const searchType = getActivityType(activityToSearch);
|
|
263
|
-
let midDate;
|
|
264
|
-
|
|
265
|
-
if (searchType === ACTIVITY_TYPES.REPLY || searchType === ACTIVITY_TYPES.EDIT) {
|
|
266
|
-
midDate = activityToSearch.parent.published;
|
|
267
|
-
} else {
|
|
268
|
-
midDate = activityToSearch.published;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return {midDate, limit: batchSize};
|
|
272
|
-
}
|
|
273
|
-
case OLDER: {
|
|
274
|
-
const maxDate = oldestPublishedDate - 1;
|
|
275
|
-
|
|
276
|
-
return {maxDate};
|
|
277
|
-
}
|
|
278
|
-
case INITIAL:
|
|
279
|
-
default: {
|
|
280
|
-
return {};
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
};
|
|
1
|
+
import {last} from 'lodash';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ACTIVITY_TYPES,
|
|
5
|
+
getParentId,
|
|
6
|
+
isNewer,
|
|
7
|
+
getActivityType,
|
|
8
|
+
sortActivitiesByPublishedDate,
|
|
9
|
+
NEWER,
|
|
10
|
+
OLDER,
|
|
11
|
+
INITIAL,
|
|
12
|
+
MID,
|
|
13
|
+
} from './activities';
|
|
14
|
+
|
|
15
|
+
export const defaultMinDisplayableActivities = 20;
|
|
16
|
+
export const minBatchSize = 10;
|
|
17
|
+
export const fetchLoopCountMax = 100;
|
|
18
|
+
export const batchSizeIncrementCount = 10;
|
|
19
|
+
|
|
20
|
+
// use accessors for ease of refactoring underlying implementation
|
|
21
|
+
/**
|
|
22
|
+
* @param {Map} destination destination map object for data. Currently a Map, but could be refactored to use any type.
|
|
23
|
+
* @param {string} key
|
|
24
|
+
* @param {any} value
|
|
25
|
+
* @returns {Map}
|
|
26
|
+
*/
|
|
27
|
+
export const setValue = (destination, key, value) => destination.set(key, value);
|
|
28
|
+
/**
|
|
29
|
+
* @param {Map} source source map object to access. Currently expects a Map, but could be refactored to use any type
|
|
30
|
+
* @param {string} key
|
|
31
|
+
* @returns {Map}
|
|
32
|
+
*/
|
|
33
|
+
export const getValue = (source, key) => source.get(key);
|
|
34
|
+
|
|
35
|
+
export const getActivityObjectsFromMap = (hashMap) =>
|
|
36
|
+
Array.from(hashMap).map(([, activity]) => activity);
|
|
37
|
+
/**
|
|
38
|
+
* creates maps for various activity types and defines handlers for working with stored activities
|
|
39
|
+
* utilizes revealing module pattern to close over state and only expose certain necessary functions for altering state
|
|
40
|
+
* @function
|
|
41
|
+
* @returns {object}
|
|
42
|
+
* getActivityHandlerByKey(activityType) - accepts a key to map to a defined activity handler
|
|
43
|
+
* getActivityByTypeAndParentId(activityType, parentId) accepts a key and a parent ID to return an activity of that type whose parent is the parentId
|
|
44
|
+
*/
|
|
45
|
+
export const activityManager = () => {
|
|
46
|
+
const replyActivityHash = new Map();
|
|
47
|
+
const editActivityHash = new Map();
|
|
48
|
+
const reactionActivityHash = new Map();
|
|
49
|
+
const reactionSelfActivityHash = new Map();
|
|
50
|
+
|
|
51
|
+
const handleNewReply = (replyAct) => {
|
|
52
|
+
const replyParentId = getParentId(replyAct);
|
|
53
|
+
const existingReplyHash = getValue(replyActivityHash, replyParentId);
|
|
54
|
+
|
|
55
|
+
if (existingReplyHash) {
|
|
56
|
+
setValue(existingReplyHash, replyAct.id, replyAct);
|
|
57
|
+
} else {
|
|
58
|
+
const replyHash = new Map();
|
|
59
|
+
|
|
60
|
+
setValue(replyHash, replyAct.id, replyAct);
|
|
61
|
+
setValue(replyActivityHash, replyParentId, replyHash);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleNewEdit = (editAct) => {
|
|
66
|
+
const isTombstone = editAct.verb === ACTIVITY_TYPES.TOMBSTONE;
|
|
67
|
+
|
|
68
|
+
// we can ignore tombstone edits in favor of the newer one
|
|
69
|
+
if (isTombstone) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const editParentId = getParentId(editAct);
|
|
74
|
+
const existingEdit = getValue(editActivityHash, editParentId);
|
|
75
|
+
|
|
76
|
+
// edited activity must be newer than what we already have
|
|
77
|
+
if (!existingEdit || isNewer(editAct, existingEdit)) {
|
|
78
|
+
setValue(editActivityHash, editParentId, editAct);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// logic is identical between reactions and reaction selfs, so handler simply passes the activity and the correct hash
|
|
83
|
+
const reactionHelper = (reactionAct, hash) => {
|
|
84
|
+
const reactionParentId = getParentId(reactionAct);
|
|
85
|
+
const existingReaction = getValue(hash, reactionParentId);
|
|
86
|
+
|
|
87
|
+
// reaction activity must be newer than what we already have
|
|
88
|
+
if (!existingReaction || isNewer(reactionAct, existingReaction)) {
|
|
89
|
+
setValue(hash, reactionParentId, reactionAct);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const handleNewReaction = (reactionAct) => {
|
|
94
|
+
reactionHelper(reactionAct, reactionActivityHash);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const handleNewReactionSelf = (reactionSelfAct) => {
|
|
98
|
+
reactionHelper(reactionSelfAct, reactionSelfActivityHash);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const getActivityHandlerByKey = (key) =>
|
|
102
|
+
({
|
|
103
|
+
[ACTIVITY_TYPES.REACTION]: handleNewReaction,
|
|
104
|
+
[ACTIVITY_TYPES.REACTION_SELF]: handleNewReactionSelf,
|
|
105
|
+
[ACTIVITY_TYPES.EDIT]: handleNewEdit,
|
|
106
|
+
[ACTIVITY_TYPES.REPLY]: handleNewReply,
|
|
107
|
+
}[key]);
|
|
108
|
+
|
|
109
|
+
const getActivityByTypeAndParentId = (type, id) =>
|
|
110
|
+
({
|
|
111
|
+
[ACTIVITY_TYPES.EDIT]: getValue(editActivityHash, id),
|
|
112
|
+
[ACTIVITY_TYPES.REPLY]: getValue(replyActivityHash, id),
|
|
113
|
+
[ACTIVITY_TYPES.REACTION]: getValue(reactionActivityHash, id),
|
|
114
|
+
[ACTIVITY_TYPES.REACTION_SELF]: getValue(reactionSelfActivityHash, id),
|
|
115
|
+
}[type]);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
getActivityHandlerByKey,
|
|
119
|
+
getActivityByTypeAndParentId,
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* encapsulates state and logic for managing oldest and newest activities
|
|
125
|
+
* @returns {object} setters and getters for activity state management
|
|
126
|
+
*/
|
|
127
|
+
export const bookendManager = () => {
|
|
128
|
+
// keep track of generator state, like what our current oldest & newest activities are
|
|
129
|
+
let oldestAct;
|
|
130
|
+
let newestAct;
|
|
131
|
+
|
|
132
|
+
const getOldestAct = () => oldestAct;
|
|
133
|
+
const getNewestAct = () => newestAct;
|
|
134
|
+
|
|
135
|
+
const setOldestAct = (act) => {
|
|
136
|
+
if (!oldestAct) {
|
|
137
|
+
oldestAct = act;
|
|
138
|
+
} else if (isNewer(oldestAct, act)) {
|
|
139
|
+
oldestAct = act;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const setNewestAct = (act) => {
|
|
144
|
+
if (!newestAct) {
|
|
145
|
+
newestAct = act;
|
|
146
|
+
} else if (isNewer(act, newestAct)) {
|
|
147
|
+
newestAct = act;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const setBookends = (activities) => {
|
|
152
|
+
const oldestActsFirst = sortActivitiesByPublishedDate(activities);
|
|
153
|
+
|
|
154
|
+
const newestInBatch = last(oldestActsFirst);
|
|
155
|
+
const oldestInBatch = oldestActsFirst[0];
|
|
156
|
+
|
|
157
|
+
setOldestAct(oldestInBatch);
|
|
158
|
+
setNewestAct(newestInBatch);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
setBookends,
|
|
163
|
+
getNewestAct,
|
|
164
|
+
getOldestAct,
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* encapsulates state and logic for when there are no more fetchable activities from convo
|
|
170
|
+
* @returns {object} setters and getters for no more activities logic
|
|
171
|
+
*/
|
|
172
|
+
export const noMoreActivitiesManager = () => {
|
|
173
|
+
// used to determine if we should continue to fetch older activities
|
|
174
|
+
// must be set per iteration, as querying newer activities is still valid when all end of convo has been reached
|
|
175
|
+
let noMoreActs = false;
|
|
176
|
+
let noOlderActs = false;
|
|
177
|
+
let noNewerActs = false;
|
|
178
|
+
|
|
179
|
+
const getNoMoreActs = () => noMoreActs;
|
|
180
|
+
|
|
181
|
+
const checkAndSetNoOlderActs = (act) => {
|
|
182
|
+
if (!noOlderActs && getActivityType(act) === ACTIVITY_TYPES.CREATE) {
|
|
183
|
+
noOlderActs = true;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const checkAndSetNoNewerActs = (activities) => {
|
|
188
|
+
if (!activities || !activities.length) {
|
|
189
|
+
noNewerActs = true;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const checkAndSetNoMoreActs = (queryType, visibleActs, currentBatchSize) => {
|
|
194
|
+
if (
|
|
195
|
+
(queryType === NEWER && noNewerActs) ||
|
|
196
|
+
((queryType === OLDER || queryType === INITIAL) && noOlderActs) ||
|
|
197
|
+
(queryType === MID && visibleActs < currentBatchSize && noOlderActs)
|
|
198
|
+
) {
|
|
199
|
+
noMoreActs = true;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
getNoMoreActs,
|
|
205
|
+
checkAndSetNoMoreActs,
|
|
206
|
+
checkAndSetNoNewerActs,
|
|
207
|
+
checkAndSetNoOlderActs,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* encapsulates state and logic for managing root activities
|
|
213
|
+
* @returns {object} setters and getters for activity state management
|
|
214
|
+
*/
|
|
215
|
+
export const rootActivityManager = () => {
|
|
216
|
+
const rootActivityHash = new Map();
|
|
217
|
+
|
|
218
|
+
const addNewRoot = (rootAct) => {
|
|
219
|
+
setValue(rootActivityHash, rootAct.id, rootAct);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const getRootActivityHash = () => rootActivityHash;
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
addNewRoot,
|
|
226
|
+
getRootActivityHash,
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const getLoopCounterFailsafe = () => {
|
|
231
|
+
let fetchLoopCount = 0;
|
|
232
|
+
|
|
233
|
+
return () => {
|
|
234
|
+
fetchLoopCount += 1;
|
|
235
|
+
if (fetchLoopCount > fetchLoopCountMax) {
|
|
236
|
+
throw new Error('max fetches reached');
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* creates activity query object
|
|
243
|
+
* @param {string} type type of query to create
|
|
244
|
+
* @param {object} queryOptions options to define query
|
|
245
|
+
* @param {string} [queryOptions.newestPublishedDate] the date of the newest fetched activity
|
|
246
|
+
* @param {string} [queryOptions.oldestPublishedDate] the date of the oldest fetched activity
|
|
247
|
+
* @param {number} [queryOptions.batchSize] the number of activities to query
|
|
248
|
+
* @param {object} [queryOptions.activityToSearch] a server activity to use to build middate query
|
|
249
|
+
* @returns {object}
|
|
250
|
+
*/
|
|
251
|
+
export const getQuery = (type, queryOptions) => {
|
|
252
|
+
const {newestPublishedDate, oldestPublishedDate, batchSize, activityToSearch = {}} = queryOptions;
|
|
253
|
+
|
|
254
|
+
switch (type) {
|
|
255
|
+
case NEWER: {
|
|
256
|
+
const sinceDate = newestPublishedDate + 1;
|
|
257
|
+
const lastActivityFirst = false;
|
|
258
|
+
|
|
259
|
+
return {sinceDate, lastActivityFirst};
|
|
260
|
+
}
|
|
261
|
+
case MID: {
|
|
262
|
+
const searchType = getActivityType(activityToSearch);
|
|
263
|
+
let midDate;
|
|
264
|
+
|
|
265
|
+
if (searchType === ACTIVITY_TYPES.REPLY || searchType === ACTIVITY_TYPES.EDIT) {
|
|
266
|
+
midDate = activityToSearch.parent.published;
|
|
267
|
+
} else {
|
|
268
|
+
midDate = activityToSearch.published;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {midDate, limit: batchSize};
|
|
272
|
+
}
|
|
273
|
+
case OLDER: {
|
|
274
|
+
const maxDate = oldestPublishedDate - 1;
|
|
275
|
+
|
|
276
|
+
return {maxDate};
|
|
277
|
+
}
|
|
278
|
+
case INITIAL:
|
|
279
|
+
default: {
|
|
280
|
+
return {};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|