@stream-io/feeds-client 0.2.8 → 0.2.10
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/CHANGELOG.md +14 -0
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react-bindings.js +1 -1
- package/dist/cjs/react-bindings.js.map +1 -1
- package/dist/es/index.mjs +3 -2
- package/dist/es/react-bindings.mjs +1 -1
- package/dist/es/react-bindings.mjs.map +1 -1
- package/dist/{index-Cfbt0DFY.js → index-C49kZoN7.js} +273 -51
- package/dist/index-C49kZoN7.js.map +1 -0
- package/dist/{index-DLC5hiNd.mjs → index-EeFSq3sq.mjs} +273 -51
- package/dist/index-EeFSq3sq.mjs.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-added.d.ts +4 -5
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-added.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts +1 -2
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts +6 -5
- package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-added.d.ts +4 -3
- package/dist/types/feed/event-handlers/comment/handle-comment-added.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-deleted.d.ts +4 -3
- package/dist/types/feed/event-handlers/comment/handle-comment-deleted.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-added.d.ts +5 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-added.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-deleted.d.ts +5 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-deleted.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-updated.d.ts +4 -3
- package/dist/types/feed/event-handlers/comment/handle-comment-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/index.d.ts +2 -1
- package/dist/types/feed/event-handlers/comment/index.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/utils/index.d.ts +2 -0
- package/dist/types/feed/event-handlers/comment/utils/index.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/comment/utils/update-comment-count.d.ts +8 -0
- package/dist/types/feed/event-handlers/comment/utils/update-comment-count.d.ts.map +1 -0
- package/dist/types/feed/feed.d.ts.map +1 -1
- package/dist/types/feeds-client/feeds-client.d.ts +19 -1
- package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
- package/dist/types/types-internal.d.ts +4 -2
- package/dist/types/types-internal.d.ts.map +1 -1
- package/dist/types/utils/ensure-exhausted.d.ts +2 -0
- package/dist/types/utils/ensure-exhausted.d.ts.map +1 -0
- package/dist/types/utils/event-triggered-by-connected-user.d.ts +6 -0
- package/dist/types/utils/event-triggered-by-connected-user.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/logger.d.ts +10 -1
- package/dist/types/utils/logger.d.ts.map +1 -1
- package/dist/types/utils/state-update-queue.d.ts +22 -2
- package/dist/types/utils/state-update-queue.d.ts.map +1 -1
- package/dist/types/utils/type-assertions.d.ts +2 -5
- package/dist/types/utils/type-assertions.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/feed/event-handlers/activity/handle-activity-reaction-added.ts +15 -10
- package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +1 -1
- package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.ts +1 -1
- package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +131 -1
- package/src/feed/event-handlers/activity/handle-activity-updated.ts +38 -15
- package/src/feed/event-handlers/comment/handle-comment-added.test.ts +131 -7
- package/src/feed/event-handlers/comment/handle-comment-added.ts +24 -4
- package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +124 -2
- package/src/feed/event-handlers/comment/handle-comment-deleted.ts +29 -3
- package/src/feed/event-handlers/comment/{handle-comment-reaction.test.ts → handle-comment-reaction-added.test.ts} +152 -138
- package/src/feed/event-handlers/comment/handle-comment-reaction-added.ts +72 -0
- package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.test.ts +343 -0
- package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.ts +74 -0
- package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +137 -1
- package/src/feed/event-handlers/comment/handle-comment-updated.ts +29 -4
- package/src/feed/event-handlers/comment/index.ts +3 -1
- package/src/feed/event-handlers/comment/utils/index.ts +1 -0
- package/src/feed/event-handlers/comment/utils/update-comment-count.test.ts +320 -0
- package/src/feed/event-handlers/comment/utils/update-comment-count.ts +51 -0
- package/src/feed/event-handlers/follow/handle-follow-deleted.ts +1 -1
- package/src/feed/feed.ts +4 -3
- package/src/feeds-client/feeds-client.ts +104 -0
- package/src/test-utils/response-generators.ts +18 -1
- package/src/types-internal.ts +4 -4
- package/src/utils/ensure-exhausted.ts +5 -0
- package/src/utils/event-triggered-by-connected-user.test.ts +73 -0
- package/src/utils/event-triggered-by-connected-user.ts +15 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/logger.ts +2 -1
- package/src/utils/state-update-queue.ts +89 -25
- package/src/utils/type-assertions.ts +2 -3
- package/dist/index-Cfbt0DFY.js.map +0 -1
- package/dist/index-DLC5hiNd.mjs.map +0 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction.d.ts +0 -4
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction.d.ts.map +0 -1
- package/src/feed/event-handlers/comment/handle-comment-reaction.ts +0 -61
|
@@ -10,8 +10,12 @@ import {
|
|
|
10
10
|
generateFeedReactionResponse,
|
|
11
11
|
generateFeedResponse,
|
|
12
12
|
generateOwnUser,
|
|
13
|
+
generateUserResponseCommonFields,
|
|
13
14
|
getHumanId,
|
|
14
|
-
} from '../../../test-utils
|
|
15
|
+
} from '../../../test-utils';
|
|
16
|
+
import { EventPayload } from '../../../types-internal';
|
|
17
|
+
import { shouldUpdateState } from '../../../utils';
|
|
18
|
+
import { ActivityResponse } from '../../../gen/models';
|
|
15
19
|
|
|
16
20
|
describe(handleActivityUpdated.name, () => {
|
|
17
21
|
let feed: Feed;
|
|
@@ -112,4 +116,130 @@ describe(handleActivityUpdated.name, () => {
|
|
|
112
116
|
const stateAfter = feed.currentState;
|
|
113
117
|
expect(stateAfter).toBe(stateBefore);
|
|
114
118
|
});
|
|
119
|
+
|
|
120
|
+
describe(`Activity updated ${shouldUpdateState.name} integration`, () => {
|
|
121
|
+
const activityId = 'reacted-activity';
|
|
122
|
+
const updatedText = 'updated-text';
|
|
123
|
+
let existingActivity: ActivityResponse;
|
|
124
|
+
let currentUserPayload: EventPayload<'feeds.activity.updated'>;
|
|
125
|
+
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
existingActivity = generateActivityResponse({
|
|
128
|
+
id: activityId,
|
|
129
|
+
text: 'original-text',
|
|
130
|
+
});
|
|
131
|
+
currentUserPayload = generateActivityUpdatedEvent({
|
|
132
|
+
activity: { ...existingActivity, text: updatedText },
|
|
133
|
+
user: generateUserResponseCommonFields({ id: currentUserId }),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
feed.state.partialNext({ activities: [existingActivity] });
|
|
137
|
+
feed.state.partialNext({ watch: true });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it(`skips update if ${shouldUpdateState.name} returns false`, () => {
|
|
141
|
+
// 1. HTTP and then WS
|
|
142
|
+
|
|
143
|
+
handleActivityUpdated.call(feed, currentUserPayload, false);
|
|
144
|
+
|
|
145
|
+
let stateBefore = feed.currentState;
|
|
146
|
+
|
|
147
|
+
handleActivityUpdated.call(feed, currentUserPayload);
|
|
148
|
+
|
|
149
|
+
let stateAfter = feed.currentState;
|
|
150
|
+
|
|
151
|
+
expect(stateAfter).toBe(stateBefore);
|
|
152
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
153
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
154
|
+
|
|
155
|
+
// 2. WS and the HTTP
|
|
156
|
+
|
|
157
|
+
handleActivityUpdated.call(feed, currentUserPayload);
|
|
158
|
+
|
|
159
|
+
stateBefore = feed.currentState;
|
|
160
|
+
|
|
161
|
+
handleActivityUpdated.call(feed, currentUserPayload, false);
|
|
162
|
+
|
|
163
|
+
stateAfter = feed.currentState;
|
|
164
|
+
|
|
165
|
+
expect(stateAfter).toBe(stateBefore);
|
|
166
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
167
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('allows update again from WS after clearing the stateUpdateQueue', () => {
|
|
171
|
+
handleActivityUpdated.call(feed, currentUserPayload);
|
|
172
|
+
|
|
173
|
+
expect(feed.currentState.activities?.[0]).toMatchObject({
|
|
174
|
+
...existingActivity,
|
|
175
|
+
text: updatedText,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Clear the queue
|
|
179
|
+
(feed as any).stateUpdateQueue.clear();
|
|
180
|
+
|
|
181
|
+
// Now update should be allowed from another WS event
|
|
182
|
+
handleActivityUpdated.call(feed, {
|
|
183
|
+
...currentUserPayload,
|
|
184
|
+
activity: {
|
|
185
|
+
...currentUserPayload.activity,
|
|
186
|
+
text: 'updated-again-text',
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(feed.currentState.activities?.[0]).toMatchObject({
|
|
191
|
+
...existingActivity,
|
|
192
|
+
text: 'updated-again-text',
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
|
|
197
|
+
handleActivityUpdated.call(feed, currentUserPayload, false);
|
|
198
|
+
|
|
199
|
+
expect(feed.currentState.activities?.[0]).toMatchObject({
|
|
200
|
+
...existingActivity,
|
|
201
|
+
text: updatedText,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Clear the queue
|
|
205
|
+
(feed as any).stateUpdateQueue.clear();
|
|
206
|
+
|
|
207
|
+
// Now update should be allowed from another WS event
|
|
208
|
+
handleActivityUpdated.call(
|
|
209
|
+
feed,
|
|
210
|
+
{
|
|
211
|
+
...currentUserPayload,
|
|
212
|
+
activity: {
|
|
213
|
+
...currentUserPayload.activity,
|
|
214
|
+
text: 'updated-again-text',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
false,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
expect(feed.currentState.activities?.[0]).toMatchObject({
|
|
221
|
+
...existingActivity,
|
|
222
|
+
text: 'updated-again-text',
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the reaction', () => {
|
|
227
|
+
const otherUserPayload = {
|
|
228
|
+
...currentUserPayload,
|
|
229
|
+
user: generateUserResponseCommonFields({ id: getHumanId() }),
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
handleActivityUpdated.call(feed, otherUserPayload);
|
|
233
|
+
|
|
234
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
235
|
+
|
|
236
|
+
handleActivityUpdated.call(feed, otherUserPayload);
|
|
237
|
+
|
|
238
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
239
|
+
expect(feed.currentState.activities?.[0]).toMatchObject({
|
|
240
|
+
...existingActivity,
|
|
241
|
+
text: updatedText,
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
115
245
|
});
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { Feed } from '../../../feed';
|
|
2
|
+
import { ActivityPinResponse, ActivityResponse } from '../../../gen/models';
|
|
3
|
+
import { EventPayload, PartializeAllBut } from '../../../types-internal';
|
|
2
4
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '../../../
|
|
7
|
-
import {
|
|
8
|
-
|
|
5
|
+
getStateUpdateQueueId,
|
|
6
|
+
shouldUpdateState,
|
|
7
|
+
updateEntityInArray,
|
|
8
|
+
} from '../../../utils';
|
|
9
|
+
import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
|
|
10
|
+
|
|
11
|
+
export type ActivityUpdatedPayload = PartializeAllBut<
|
|
12
|
+
EventPayload<'feeds.activity.updated'>,
|
|
13
|
+
'activity'
|
|
14
|
+
>;
|
|
9
15
|
|
|
10
16
|
const sharedUpdateActivity = ({
|
|
11
17
|
currentActivity,
|
|
12
18
|
event,
|
|
13
19
|
}: {
|
|
14
20
|
currentActivity: ActivityResponse;
|
|
15
|
-
event:
|
|
21
|
+
event: ActivityUpdatedPayload;
|
|
16
22
|
}) => {
|
|
17
23
|
return {
|
|
18
24
|
...event.activity,
|
|
@@ -22,7 +28,7 @@ const sharedUpdateActivity = ({
|
|
|
22
28
|
};
|
|
23
29
|
|
|
24
30
|
export const updateActivityInState = (
|
|
25
|
-
event:
|
|
31
|
+
event: ActivityUpdatedPayload,
|
|
26
32
|
activities: ActivityResponse[] | undefined,
|
|
27
33
|
) =>
|
|
28
34
|
updateEntityInArray({
|
|
@@ -36,7 +42,7 @@ export const updateActivityInState = (
|
|
|
36
42
|
});
|
|
37
43
|
|
|
38
44
|
export const updatePinnedActivityInState = (
|
|
39
|
-
event:
|
|
45
|
+
event: ActivityUpdatedPayload,
|
|
40
46
|
pinnedActivities: ActivityPinResponse[] | undefined,
|
|
41
47
|
) =>
|
|
42
48
|
updateEntityInArray({
|
|
@@ -62,23 +68,40 @@ export const updatePinnedActivityInState = (
|
|
|
62
68
|
|
|
63
69
|
export function handleActivityUpdated(
|
|
64
70
|
this: Feed,
|
|
65
|
-
|
|
71
|
+
payload: ActivityUpdatedPayload,
|
|
72
|
+
fromWs?: boolean,
|
|
66
73
|
) {
|
|
74
|
+
if (
|
|
75
|
+
!shouldUpdateState({
|
|
76
|
+
stateUpdateQueueId: getStateUpdateQueueId(payload, 'activity-updated'),
|
|
77
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
78
|
+
watch: this.currentState.watch,
|
|
79
|
+
fromWs,
|
|
80
|
+
isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(
|
|
81
|
+
this,
|
|
82
|
+
payload,
|
|
83
|
+
),
|
|
84
|
+
})
|
|
85
|
+
) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
67
88
|
const {
|
|
68
89
|
activities: currentActivities,
|
|
69
90
|
pinned_activities: currentPinnedActivities,
|
|
70
91
|
} = this.currentState;
|
|
71
92
|
|
|
72
93
|
const [result1, result2] = [
|
|
73
|
-
|
|
74
|
-
|
|
94
|
+
this.hasActivity(payload.activity.id)
|
|
95
|
+
? updateActivityInState(payload, currentActivities)
|
|
96
|
+
: undefined,
|
|
97
|
+
updatePinnedActivityInState(payload, currentPinnedActivities),
|
|
75
98
|
];
|
|
76
99
|
|
|
77
|
-
if (result1
|
|
78
|
-
this.client.hydratePollCache([
|
|
100
|
+
if (result1?.changed || result2.changed) {
|
|
101
|
+
this.client.hydratePollCache([payload.activity]);
|
|
79
102
|
|
|
80
103
|
this.state.partialNext({
|
|
81
|
-
activities: result1.entities,
|
|
104
|
+
activities: result1?.changed ? result1.entities : currentActivities,
|
|
82
105
|
pinned_activities: result2.entities,
|
|
83
106
|
});
|
|
84
107
|
}
|
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
getHumanId,
|
|
9
9
|
generateCommentAddedEvent,
|
|
10
10
|
generateCommentResponse,
|
|
11
|
-
|
|
11
|
+
generateUserResponseCommonFields,
|
|
12
|
+
} from '../../../test-utils';
|
|
13
|
+
import { CommentResponse, UserResponseCommonFields } from '../../../gen/models';
|
|
14
|
+
import { EventPayload } from '../../../types-internal';
|
|
15
|
+
import { shouldUpdateState } from '../../../utils';
|
|
12
16
|
|
|
13
17
|
describe(handleCommentAdded.name, () => {
|
|
14
18
|
let feed: Feed;
|
|
@@ -64,9 +68,9 @@ describe(handleCommentAdded.name, () => {
|
|
|
64
68
|
expect(stateAfter.comments_by_entity_id[activityId]!.comments).toHaveLength(
|
|
65
69
|
2,
|
|
66
70
|
);
|
|
67
|
-
expect(
|
|
68
|
-
|
|
69
|
-
)
|
|
71
|
+
expect(stateAfter.comments_by_entity_id[activityId]!.comments!.at(-1)).toBe(
|
|
72
|
+
event.comment,
|
|
73
|
+
);
|
|
70
74
|
});
|
|
71
75
|
|
|
72
76
|
it('prepends a new comment when pagination.sort is "last"', () => {
|
|
@@ -97,9 +101,9 @@ describe(handleCommentAdded.name, () => {
|
|
|
97
101
|
expect(stateAfter.comments_by_entity_id[activityId]!.comments).toHaveLength(
|
|
98
102
|
2,
|
|
99
103
|
);
|
|
100
|
-
expect(
|
|
101
|
-
|
|
102
|
-
)
|
|
104
|
+
expect(stateAfter.comments_by_entity_id[activityId]!.comments!.at(0)).toBe(
|
|
105
|
+
event.comment,
|
|
106
|
+
);
|
|
103
107
|
});
|
|
104
108
|
|
|
105
109
|
it('stores the comment in the correct parent entity state (prefers parent_id)', () => {
|
|
@@ -144,4 +148,124 @@ describe(handleCommentAdded.name, () => {
|
|
|
144
148
|
const stateAfter = feed.currentState;
|
|
145
149
|
expect(stateAfter).toBe(stateBefore);
|
|
146
150
|
});
|
|
151
|
+
|
|
152
|
+
describe(`Comment added ${shouldUpdateState.name} integration`, () => {
|
|
153
|
+
let currentUserPayload: EventPayload<'feeds.comment.added'>;
|
|
154
|
+
let existingComment: CommentResponse;
|
|
155
|
+
let newComment: CommentResponse;
|
|
156
|
+
let commentId: string;
|
|
157
|
+
|
|
158
|
+
beforeEach(() => {
|
|
159
|
+
commentId = `comment-${getHumanId()}`;
|
|
160
|
+
existingComment = generateCommentResponse({
|
|
161
|
+
id: commentId,
|
|
162
|
+
object_id: activityId,
|
|
163
|
+
});
|
|
164
|
+
newComment = generateCommentResponse({
|
|
165
|
+
id: `comment-${getHumanId()}`,
|
|
166
|
+
object_id: activityId,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
currentUserPayload = generateCommentAddedEvent({
|
|
170
|
+
comment: newComment,
|
|
171
|
+
user: client.state.getLatestValue()
|
|
172
|
+
.connected_user as UserResponseCommonFields,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
feed.state.partialNext({
|
|
176
|
+
comments_by_entity_id: {
|
|
177
|
+
[activityId]: {
|
|
178
|
+
comments: [existingComment],
|
|
179
|
+
pagination: { sort: 'first' },
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
feed.state.partialNext({ watch: true });
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it(`skips update if ${shouldUpdateState.name} returns false`, () => {
|
|
187
|
+
// 1. HTTP and then WS
|
|
188
|
+
|
|
189
|
+
handleCommentAdded.call(feed, currentUserPayload, false);
|
|
190
|
+
|
|
191
|
+
let stateBefore = feed.currentState;
|
|
192
|
+
|
|
193
|
+
handleCommentAdded.call(feed, currentUserPayload);
|
|
194
|
+
|
|
195
|
+
let stateAfter = feed.currentState;
|
|
196
|
+
|
|
197
|
+
expect(stateAfter).toBe(stateBefore);
|
|
198
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
199
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
200
|
+
|
|
201
|
+
// 2. WS and the HTTP
|
|
202
|
+
|
|
203
|
+
handleCommentAdded.call(feed, currentUserPayload);
|
|
204
|
+
|
|
205
|
+
stateBefore = feed.currentState;
|
|
206
|
+
|
|
207
|
+
handleCommentAdded.call(feed, currentUserPayload, false);
|
|
208
|
+
|
|
209
|
+
stateAfter = feed.currentState;
|
|
210
|
+
|
|
211
|
+
expect(stateAfter).toBe(stateBefore);
|
|
212
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
213
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('allows update again from WS after clearing the stateUpdateQueue', () => {
|
|
217
|
+
handleCommentAdded.call(feed, currentUserPayload);
|
|
218
|
+
|
|
219
|
+
// Clear the queue
|
|
220
|
+
(feed as any).stateUpdateQueue.clear();
|
|
221
|
+
|
|
222
|
+
// Now update should be allowed from another WS event
|
|
223
|
+
handleCommentAdded.call(feed, currentUserPayload);
|
|
224
|
+
|
|
225
|
+
const comments =
|
|
226
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
227
|
+
const [latestComment] = (comments ?? []).toReversed();
|
|
228
|
+
|
|
229
|
+
expect(comments?.length).toEqual(3);
|
|
230
|
+
expect(latestComment).toMatchObject(newComment);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
|
|
234
|
+
handleCommentAdded.call(feed, currentUserPayload, false);
|
|
235
|
+
|
|
236
|
+
// Clear the queue
|
|
237
|
+
(feed as any).stateUpdateQueue.clear();
|
|
238
|
+
|
|
239
|
+
// Now update should be allowed from another HTTP response
|
|
240
|
+
handleCommentAdded.call(feed, currentUserPayload, false);
|
|
241
|
+
|
|
242
|
+
const comments =
|
|
243
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
244
|
+
const [latestComment] = (comments ?? []).toReversed();
|
|
245
|
+
|
|
246
|
+
expect(comments?.length).toEqual(3);
|
|
247
|
+
expect(latestComment).toMatchObject(newComment);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the comment reaction deletion', () => {
|
|
251
|
+
const otherUserPayload = generateCommentAddedEvent({
|
|
252
|
+
comment: newComment,
|
|
253
|
+
user: generateUserResponseCommonFields({ id: getHumanId() }),
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
handleCommentAdded.call(feed, otherUserPayload);
|
|
257
|
+
|
|
258
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
259
|
+
|
|
260
|
+
handleCommentAdded.call(feed, otherUserPayload);
|
|
261
|
+
|
|
262
|
+
const comments =
|
|
263
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
264
|
+
const [latestComment] = (comments ?? []).toReversed();
|
|
265
|
+
|
|
266
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
267
|
+
expect(comments?.length).toEqual(3);
|
|
268
|
+
expect(latestComment).toMatchObject(newComment);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
147
271
|
});
|
|
@@ -1,13 +1,33 @@
|
|
|
1
|
-
import type { Feed } from '
|
|
2
|
-
import type { EventPayload } from '../../../types-internal';
|
|
1
|
+
import type { Feed } from '../../feed';
|
|
2
|
+
import type { EventPayload, PartializeAllBut } from '../../../types-internal';
|
|
3
|
+
import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
|
|
4
|
+
import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
|
|
5
|
+
|
|
6
|
+
export type CommentAddedPayload = PartializeAllBut<
|
|
7
|
+
EventPayload<'feeds.comment.added'>,
|
|
8
|
+
'comment'
|
|
9
|
+
>;
|
|
3
10
|
|
|
4
11
|
export function handleCommentAdded(
|
|
5
12
|
this: Feed,
|
|
6
|
-
|
|
13
|
+
payload: CommentAddedPayload,
|
|
14
|
+
fromWs?: boolean,
|
|
7
15
|
) {
|
|
8
|
-
const { comment } =
|
|
16
|
+
const { comment } = payload;
|
|
9
17
|
const entityId = comment.parent_id ?? comment.object_id;
|
|
10
18
|
|
|
19
|
+
if (
|
|
20
|
+
!shouldUpdateState({
|
|
21
|
+
stateUpdateQueueId: getStateUpdateQueueId(payload, 'comment-created'),
|
|
22
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
23
|
+
watch: this.currentState.watch,
|
|
24
|
+
fromWs,
|
|
25
|
+
isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(this, payload),
|
|
26
|
+
})
|
|
27
|
+
) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
11
31
|
this.state.next((currentState) => {
|
|
12
32
|
const entityState = currentState.comments_by_entity_id[entityId];
|
|
13
33
|
|
|
@@ -6,9 +6,12 @@ import {
|
|
|
6
6
|
generateCommentDeletedEvent,
|
|
7
7
|
generateCommentResponse,
|
|
8
8
|
generateFeedResponse,
|
|
9
|
-
generateOwnUser,
|
|
9
|
+
generateOwnUser, generateUserResponseCommonFields,
|
|
10
10
|
getHumanId,
|
|
11
|
-
} from '../../../test-utils
|
|
11
|
+
} from '../../../test-utils';
|
|
12
|
+
import { CommentResponse, UserResponseCommonFields } from '../../../gen/models';
|
|
13
|
+
import { shouldUpdateState } from '../../../utils';
|
|
14
|
+
import { EventPayload } from '../../../types-internal';
|
|
12
15
|
|
|
13
16
|
describe(handleCommentDeleted.name, () => {
|
|
14
17
|
let feed: Feed;
|
|
@@ -130,4 +133,123 @@ describe(handleCommentDeleted.name, () => {
|
|
|
130
133
|
|
|
131
134
|
expect(stateAfter).toBe(stateBefore);
|
|
132
135
|
});
|
|
136
|
+
|
|
137
|
+
describe(`Comment deleted ${shouldUpdateState.name} integration`, () => {
|
|
138
|
+
let currentUserPayload: EventPayload<'feeds.comment.deleted'>;
|
|
139
|
+
let existingComment: CommentResponse;
|
|
140
|
+
let commentToDelete: CommentResponse;
|
|
141
|
+
let commentId: string;
|
|
142
|
+
|
|
143
|
+
beforeEach(() => {
|
|
144
|
+
commentId = `comment-${getHumanId()}`;
|
|
145
|
+
existingComment = generateCommentResponse({
|
|
146
|
+
id: commentId,
|
|
147
|
+
object_id: activityId,
|
|
148
|
+
});
|
|
149
|
+
commentToDelete = generateCommentResponse({
|
|
150
|
+
id: `comment-${getHumanId()}`,
|
|
151
|
+
object_id: activityId,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
currentUserPayload = generateCommentDeletedEvent({
|
|
155
|
+
comment: commentToDelete,
|
|
156
|
+
user: client.state.getLatestValue().connected_user as UserResponseCommonFields,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
feed.state.partialNext({
|
|
160
|
+
comments_by_entity_id: {
|
|
161
|
+
[activityId]: {
|
|
162
|
+
comments: [existingComment, commentToDelete],
|
|
163
|
+
pagination: { sort: 'first' },
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
feed.state.partialNext({ watch: true });
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it(`skips update if ${shouldUpdateState.name} returns false`, () => {
|
|
171
|
+
// 1. HTTP and then WS
|
|
172
|
+
|
|
173
|
+
handleCommentDeleted.call(feed, currentUserPayload, false);
|
|
174
|
+
|
|
175
|
+
let stateBefore = feed.currentState;
|
|
176
|
+
|
|
177
|
+
handleCommentDeleted.call(feed, currentUserPayload);
|
|
178
|
+
|
|
179
|
+
let stateAfter = feed.currentState;
|
|
180
|
+
|
|
181
|
+
expect(stateAfter).toBe(stateBefore);
|
|
182
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
183
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
184
|
+
|
|
185
|
+
// 2. WS and the HTTP
|
|
186
|
+
|
|
187
|
+
handleCommentDeleted.call(feed, currentUserPayload);
|
|
188
|
+
|
|
189
|
+
stateBefore = feed.currentState;
|
|
190
|
+
|
|
191
|
+
handleCommentDeleted.call(feed, currentUserPayload, false);
|
|
192
|
+
|
|
193
|
+
stateAfter = feed.currentState;
|
|
194
|
+
|
|
195
|
+
expect(stateAfter).toBe(stateBefore);
|
|
196
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
197
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('allows update again from WS after clearing the stateUpdateQueue', () => {
|
|
201
|
+
handleCommentDeleted.call(feed, currentUserPayload);
|
|
202
|
+
|
|
203
|
+
// Clear the queue
|
|
204
|
+
(feed as any).stateUpdateQueue.clear();
|
|
205
|
+
|
|
206
|
+
// Now update should be allowed from another WS event
|
|
207
|
+
handleCommentDeleted.call(feed, currentUserPayload);
|
|
208
|
+
|
|
209
|
+
const comments =
|
|
210
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
211
|
+
const [latestComment] = (comments ?? []).toReversed();
|
|
212
|
+
|
|
213
|
+
expect(comments?.length).toEqual(1);
|
|
214
|
+
expect(latestComment).toBe(existingComment);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
|
|
218
|
+
handleCommentDeleted.call(feed, currentUserPayload, false);
|
|
219
|
+
|
|
220
|
+
// Clear the queue
|
|
221
|
+
(feed as any).stateUpdateQueue.clear();
|
|
222
|
+
|
|
223
|
+
// Now update should be allowed from another HTTP response
|
|
224
|
+
handleCommentDeleted.call(feed, currentUserPayload, false);
|
|
225
|
+
|
|
226
|
+
const comments =
|
|
227
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
228
|
+
const [latestComment] = (comments ?? []).toReversed();
|
|
229
|
+
|
|
230
|
+
expect(comments?.length).toEqual(1);
|
|
231
|
+
expect(latestComment).toBe(existingComment);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the comment reaction deletion', () => {
|
|
235
|
+
const otherUserPayload = generateCommentDeletedEvent({
|
|
236
|
+
comment: commentToDelete,
|
|
237
|
+
user: generateUserResponseCommonFields({ id: getHumanId() })
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
handleCommentDeleted.call(feed, otherUserPayload);
|
|
241
|
+
|
|
242
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
243
|
+
|
|
244
|
+
handleCommentDeleted.call(feed, otherUserPayload);
|
|
245
|
+
|
|
246
|
+
const comments =
|
|
247
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
248
|
+
const [latestComment] = (comments ?? []).toReversed();
|
|
249
|
+
|
|
250
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
251
|
+
expect(comments?.length).toEqual(1);
|
|
252
|
+
expect(latestComment).toBe(existingComment);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
133
255
|
});
|
|
@@ -1,12 +1,38 @@
|
|
|
1
|
-
import { Feed } from '
|
|
2
|
-
import { EventPayload } from '../../../types-internal';
|
|
1
|
+
import { Feed } from '../../feed';
|
|
2
|
+
import { EventPayload, type PartializeAllBut } from '../../../types-internal';
|
|
3
|
+
import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
|
|
4
|
+
import {
|
|
5
|
+
eventTriggeredByConnectedUser
|
|
6
|
+
} from '../../../utils/event-triggered-by-connected-user';
|
|
7
|
+
|
|
8
|
+
export type CommentDeletedPayload = PartializeAllBut<
|
|
9
|
+
EventPayload<'feeds.comment.deleted'>,
|
|
10
|
+
'comment'
|
|
11
|
+
>;
|
|
3
12
|
|
|
4
13
|
export function handleCommentDeleted(
|
|
5
14
|
this: Feed,
|
|
6
|
-
|
|
15
|
+
payload: CommentDeletedPayload,
|
|
16
|
+
fromWs?: boolean,
|
|
7
17
|
) {
|
|
18
|
+
const { comment } = payload;
|
|
8
19
|
const entityId = comment.parent_id ?? comment.object_id;
|
|
9
20
|
|
|
21
|
+
if (
|
|
22
|
+
!shouldUpdateState({
|
|
23
|
+
stateUpdateQueueId: getStateUpdateQueueId(
|
|
24
|
+
payload,
|
|
25
|
+
'comment-deleted',
|
|
26
|
+
),
|
|
27
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
28
|
+
watch: this.currentState.watch,
|
|
29
|
+
fromWs,
|
|
30
|
+
isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(this, payload),
|
|
31
|
+
})
|
|
32
|
+
) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
10
36
|
this.state.next((currentState) => {
|
|
11
37
|
let newCommentsByEntityId:
|
|
12
38
|
| typeof currentState.comments_by_entity_id
|