@stream-io/feeds-client 0.2.15 → 0.2.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/CHANGELOG.md +20 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/react-bindings.js +1 -1
- package/dist/es/index.mjs +2 -2
- package/dist/es/react-bindings.mjs +1 -1
- package/dist/{index-BSzSBlMh.mjs → index-BZL77zNq.mjs} +184 -27
- package/dist/index-BZL77zNq.mjs.map +1 -0
- package/dist/{index-DRX66SIx.js → index-nq6SDtbt.js} +184 -27
- package/dist/index-nq6SDtbt.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-updated.d.ts +14 -0
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-updated.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/activity/index.d.ts +1 -0
- package/dist/types/feed/event-handlers/activity/index.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-added.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-updated.d.ts +6 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-updated.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/comment/index.d.ts +1 -0
- package/dist/types/feed/event-handlers/comment/index.d.ts.map +1 -1
- package/dist/types/feed/feed.d.ts.map +1 -1
- package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
- package/dist/types/gen/feeds/FeedsApi.d.ts +4 -1
- package/dist/types/gen/feeds/FeedsApi.d.ts.map +1 -1
- package/dist/types/gen/models/index.d.ts +7 -0
- package/dist/types/gen/models/index.d.ts.map +1 -1
- package/dist/types/utils/state-update-queue.d.ts +5 -1
- package/dist/types/utils/state-update-queue.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/feed/event-handlers/activity/handle-activity-reaction-updated.test.ts +282 -0
- package/src/feed/event-handlers/activity/handle-activity-reaction-updated.ts +140 -0
- package/src/feed/event-handlers/activity/index.ts +1 -0
- package/src/feed/event-handlers/comment/handle-comment-reaction-added.ts +1 -2
- package/src/feed/event-handlers/comment/handle-comment-reaction-updated.test.ts +350 -0
- package/src/feed/event-handlers/comment/handle-comment-reaction-updated.ts +72 -0
- package/src/feed/event-handlers/comment/index.ts +1 -1
- package/src/feed/feed.ts +4 -2
- package/src/feeds-client/feeds-client.ts +15 -3
- package/src/gen/feeds/FeedsApi.ts +28 -0
- package/src/gen/models/index.ts +10 -0
- package/src/test-utils/response-generators.ts +52 -0
- package/src/utils/state-update-queue.ts +14 -2
- package/dist/index-BSzSBlMh.mjs.map +0 -1
- package/dist/index-DRX66SIx.js.map +0 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { Feed, handleCommentReactionUpdated } from '../../../feed';
|
|
3
|
+
import { FeedsClient } from '../../../feeds-client';
|
|
4
|
+
import {
|
|
5
|
+
generateCommentResponse,
|
|
6
|
+
generateFeedResponse,
|
|
7
|
+
generateOwnUser,
|
|
8
|
+
getHumanId,
|
|
9
|
+
generateFeedReactionResponse,
|
|
10
|
+
generateCommentReactionUpdatedEvent,
|
|
11
|
+
} from '../../../test-utils';
|
|
12
|
+
import type {
|
|
13
|
+
CommentResponse,
|
|
14
|
+
FeedsReactionResponse,
|
|
15
|
+
} from '../../../gen/models';
|
|
16
|
+
import { shouldUpdateState } from '../../../utils';
|
|
17
|
+
import type { EventPayload } from '../../../types-internal';
|
|
18
|
+
|
|
19
|
+
describe(handleCommentReactionUpdated.name, () => {
|
|
20
|
+
let feed: Feed;
|
|
21
|
+
let client: FeedsClient;
|
|
22
|
+
let currentUserId: string;
|
|
23
|
+
let activityId: string;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
client = new FeedsClient('mock-api-key');
|
|
27
|
+
currentUserId = getHumanId();
|
|
28
|
+
client.state.partialNext({
|
|
29
|
+
connected_user: generateOwnUser({ id: currentUserId }),
|
|
30
|
+
});
|
|
31
|
+
const feedResponse = generateFeedResponse({
|
|
32
|
+
id: 'main',
|
|
33
|
+
group_id: 'user',
|
|
34
|
+
created_by: { id: currentUserId },
|
|
35
|
+
});
|
|
36
|
+
feed = new Feed(
|
|
37
|
+
client,
|
|
38
|
+
feedResponse.group_id,
|
|
39
|
+
feedResponse.id,
|
|
40
|
+
feedResponse,
|
|
41
|
+
);
|
|
42
|
+
activityId = `activity-${getHumanId()}`;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('updates own_reactions for current user and updates comment fields from the event', () => {
|
|
46
|
+
const existingCommentId = `comment-${getHumanId()}`;
|
|
47
|
+
const existingReaction = generateFeedReactionResponse({
|
|
48
|
+
type: 'like',
|
|
49
|
+
user: { id: currentUserId },
|
|
50
|
+
activity_id: activityId,
|
|
51
|
+
comment_id: existingCommentId,
|
|
52
|
+
});
|
|
53
|
+
const existingComment = generateCommentResponse({
|
|
54
|
+
id: existingCommentId,
|
|
55
|
+
object_id: activityId,
|
|
56
|
+
latest_reactions: [],
|
|
57
|
+
reaction_groups: {},
|
|
58
|
+
own_reactions: [existingReaction],
|
|
59
|
+
});
|
|
60
|
+
feed.state.partialNext({
|
|
61
|
+
comments_by_entity_id: {
|
|
62
|
+
[activityId]: {
|
|
63
|
+
comments: [existingComment],
|
|
64
|
+
pagination: { sort: 'first' },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const event = generateCommentReactionUpdatedEvent({
|
|
70
|
+
comment: {
|
|
71
|
+
id: existingComment.id,
|
|
72
|
+
object_id: activityId,
|
|
73
|
+
latest_reactions: [],
|
|
74
|
+
reaction_groups: {},
|
|
75
|
+
},
|
|
76
|
+
reaction: {
|
|
77
|
+
type: 'downvote',
|
|
78
|
+
user: { id: currentUserId },
|
|
79
|
+
activity_id: activityId,
|
|
80
|
+
comment_id: existingCommentId,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const stateBefore = feed.currentState;
|
|
85
|
+
const [initialReaction] = (stateBefore.comments_by_entity_id[activityId]
|
|
86
|
+
?.comments ?? [])[0].own_reactions;
|
|
87
|
+
expect(
|
|
88
|
+
stateBefore.comments_by_entity_id[activityId]?.comments?.[0]
|
|
89
|
+
?.own_reactions,
|
|
90
|
+
).toHaveLength(1);
|
|
91
|
+
expect(initialReaction).toBe(existingReaction);
|
|
92
|
+
|
|
93
|
+
handleCommentReactionUpdated.call(feed, event);
|
|
94
|
+
|
|
95
|
+
const stateAfter = feed.currentState;
|
|
96
|
+
const [updated] = stateAfter.comments_by_entity_id[activityId]!.comments!;
|
|
97
|
+
expect(updated.own_reactions).toHaveLength(1);
|
|
98
|
+
expect(updated.own_reactions[0]).toBe(event.reaction);
|
|
99
|
+
// ensure we used event's latest_reactions & reaction_groups (Object.is check)
|
|
100
|
+
expect(updated.latest_reactions).toBe(event.comment.latest_reactions);
|
|
101
|
+
expect(updated.reaction_groups).toBe(event.comment.reaction_groups);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('does modify own_reactions if the target reaction belongs to another user', () => {
|
|
105
|
+
const existingComment = generateCommentResponse({
|
|
106
|
+
object_id: activityId,
|
|
107
|
+
latest_reactions: [],
|
|
108
|
+
reaction_groups: {},
|
|
109
|
+
own_reactions: [],
|
|
110
|
+
});
|
|
111
|
+
feed.state.partialNext({
|
|
112
|
+
comments_by_entity_id: {
|
|
113
|
+
[activityId]: {
|
|
114
|
+
comments: [existingComment],
|
|
115
|
+
pagination: { sort: 'first' },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const event = generateCommentReactionUpdatedEvent({
|
|
121
|
+
comment: {
|
|
122
|
+
id: existingComment.id,
|
|
123
|
+
object_id: activityId,
|
|
124
|
+
latest_reactions: [],
|
|
125
|
+
reaction_groups: {},
|
|
126
|
+
},
|
|
127
|
+
reaction: {
|
|
128
|
+
type: 'laugh',
|
|
129
|
+
user: { id: 'other-user' },
|
|
130
|
+
activity_id: activityId,
|
|
131
|
+
comment_id: existingComment.id,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
handleCommentReactionUpdated.call(feed, event);
|
|
136
|
+
const stateAfter = feed.currentState;
|
|
137
|
+
const [updated] = stateAfter.comments_by_entity_id[activityId]!.comments!;
|
|
138
|
+
expect(updated.own_reactions).toHaveLength(0);
|
|
139
|
+
expect(updated.latest_reactions).toBe(event.comment.latest_reactions);
|
|
140
|
+
expect(updated.reaction_groups).toBe(event.comment.reaction_groups);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('updates the proper entity state (prefers parent_id)', () => {
|
|
144
|
+
const parentId = `comment-${getHumanId()}`;
|
|
145
|
+
const existingCommentId = `comment-${getHumanId()}`;
|
|
146
|
+
const existingReaction = generateFeedReactionResponse({
|
|
147
|
+
type: 'like',
|
|
148
|
+
user: { id: currentUserId },
|
|
149
|
+
activity_id: activityId,
|
|
150
|
+
comment_id: existingCommentId,
|
|
151
|
+
});
|
|
152
|
+
const existingComment = generateCommentResponse({
|
|
153
|
+
id: existingCommentId,
|
|
154
|
+
object_id: activityId,
|
|
155
|
+
parent_id: parentId,
|
|
156
|
+
latest_reactions: [],
|
|
157
|
+
reaction_groups: {},
|
|
158
|
+
own_reactions: [existingReaction],
|
|
159
|
+
});
|
|
160
|
+
feed.state.partialNext({
|
|
161
|
+
comments_by_entity_id: {
|
|
162
|
+
[parentId]: {
|
|
163
|
+
comments: [existingComment],
|
|
164
|
+
pagination: { sort: 'first' },
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const addedEvent = generateCommentReactionUpdatedEvent({
|
|
170
|
+
comment: {
|
|
171
|
+
id: existingComment.id,
|
|
172
|
+
object_id: activityId,
|
|
173
|
+
parent_id: parentId,
|
|
174
|
+
latest_reactions: [],
|
|
175
|
+
reaction_groups: {},
|
|
176
|
+
},
|
|
177
|
+
reaction: {
|
|
178
|
+
type: 'downvote',
|
|
179
|
+
user: { id: currentUserId },
|
|
180
|
+
activity_id: activityId,
|
|
181
|
+
comment_id: existingComment.id,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const stateBefore = feed.currentState;
|
|
186
|
+
expect(
|
|
187
|
+
stateBefore.comments_by_entity_id[parentId]?.comments?.[0]?.own_reactions,
|
|
188
|
+
).toHaveLength(1);
|
|
189
|
+
const [initialReaction] = (stateBefore.comments_by_entity_id[parentId]
|
|
190
|
+
?.comments ?? [])[0].own_reactions;
|
|
191
|
+
expect(initialReaction).toBe(existingReaction);
|
|
192
|
+
|
|
193
|
+
handleCommentReactionUpdated.call(feed, addedEvent);
|
|
194
|
+
const stateAfter1 = feed.currentState;
|
|
195
|
+
const [updated1] = stateAfter1.comments_by_entity_id[parentId]!.comments!;
|
|
196
|
+
expect(updated1.own_reactions).toHaveLength(1);
|
|
197
|
+
expect(updated1.own_reactions[0]).toBe(addedEvent.reaction);
|
|
198
|
+
expect(updated1.latest_reactions).toBe(addedEvent.comment.latest_reactions);
|
|
199
|
+
expect(updated1.reaction_groups).toBe(addedEvent.comment.reaction_groups);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('does nothing if comment is not found in state', () => {
|
|
203
|
+
const addedEvent = generateCommentReactionUpdatedEvent({
|
|
204
|
+
comment: { object_id: activityId },
|
|
205
|
+
reaction: { user: { id: currentUserId } },
|
|
206
|
+
});
|
|
207
|
+
const stateBefore = feed.currentState;
|
|
208
|
+
|
|
209
|
+
handleCommentReactionUpdated.call(feed, addedEvent);
|
|
210
|
+
const stateAfter = feed.currentState;
|
|
211
|
+
expect(stateAfter).toBe(stateBefore);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe(`Comment reaction updated ${shouldUpdateState.name} integration`, () => {
|
|
215
|
+
let currentUserPayload: EventPayload<'feeds.comment.reaction.updated'>;
|
|
216
|
+
let existingComment: CommentResponse;
|
|
217
|
+
let existingReaction: FeedsReactionResponse;
|
|
218
|
+
let newReaction: FeedsReactionResponse;
|
|
219
|
+
let commentId: string;
|
|
220
|
+
|
|
221
|
+
beforeEach(() => {
|
|
222
|
+
commentId = `comment-${getHumanId()}`;
|
|
223
|
+
existingReaction = generateFeedReactionResponse({
|
|
224
|
+
type: 'heart',
|
|
225
|
+
user: { id: currentUserId },
|
|
226
|
+
activity_id: activityId,
|
|
227
|
+
comment_id: commentId,
|
|
228
|
+
});
|
|
229
|
+
newReaction = generateFeedReactionResponse({
|
|
230
|
+
type: 'like',
|
|
231
|
+
user: { id: currentUserId },
|
|
232
|
+
activity_id: activityId,
|
|
233
|
+
comment_id: commentId,
|
|
234
|
+
});
|
|
235
|
+
existingComment = generateCommentResponse({
|
|
236
|
+
id: commentId,
|
|
237
|
+
object_id: activityId,
|
|
238
|
+
own_reactions: [existingReaction],
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
currentUserPayload = generateCommentReactionUpdatedEvent({
|
|
242
|
+
comment: existingComment,
|
|
243
|
+
reaction: newReaction,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
feed.state.partialNext({
|
|
247
|
+
comments_by_entity_id: {
|
|
248
|
+
[activityId]: {
|
|
249
|
+
comments: [existingComment],
|
|
250
|
+
pagination: { sort: 'first' },
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
feed.state.partialNext({ watch: true });
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it(`skips update if ${shouldUpdateState.name} returns false`, () => {
|
|
258
|
+
// 1. HTTP and then WS
|
|
259
|
+
|
|
260
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload, false);
|
|
261
|
+
|
|
262
|
+
let stateBefore = feed.currentState;
|
|
263
|
+
|
|
264
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload);
|
|
265
|
+
|
|
266
|
+
let stateAfter = feed.currentState;
|
|
267
|
+
|
|
268
|
+
expect(stateAfter).toBe(stateBefore);
|
|
269
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
270
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
271
|
+
|
|
272
|
+
// 2. WS and the HTTP
|
|
273
|
+
|
|
274
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload);
|
|
275
|
+
|
|
276
|
+
stateBefore = feed.currentState;
|
|
277
|
+
|
|
278
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload, false);
|
|
279
|
+
|
|
280
|
+
stateAfter = feed.currentState;
|
|
281
|
+
|
|
282
|
+
expect(stateAfter).toBe(stateBefore);
|
|
283
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
284
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('allows update again from WS after clearing the stateUpdateQueue', () => {
|
|
288
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload);
|
|
289
|
+
|
|
290
|
+
// Clear the queue
|
|
291
|
+
(feed as any).stateUpdateQueue.clear();
|
|
292
|
+
|
|
293
|
+
// Now update should be allowed from another WS event
|
|
294
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload);
|
|
295
|
+
|
|
296
|
+
const comments =
|
|
297
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
298
|
+
const comment = comments?.find((a) => a.id === commentId);
|
|
299
|
+
const [latestReaction] = (comment?.own_reactions ?? []).toReversed();
|
|
300
|
+
|
|
301
|
+
expect(comment?.own_reactions.length).toEqual(1);
|
|
302
|
+
expect(latestReaction).toMatchObject(newReaction);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
|
|
306
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload, false);
|
|
307
|
+
|
|
308
|
+
// Clear the queue
|
|
309
|
+
(feed as any).stateUpdateQueue.clear();
|
|
310
|
+
|
|
311
|
+
// Now update should be allowed from another HTTP response
|
|
312
|
+
handleCommentReactionUpdated.call(feed, currentUserPayload, false);
|
|
313
|
+
|
|
314
|
+
const comments =
|
|
315
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
316
|
+
const comment = comments?.find((a) => a.id === commentId);
|
|
317
|
+
const [latestReaction] = (comment?.own_reactions ?? []).toReversed();
|
|
318
|
+
|
|
319
|
+
expect(comment?.own_reactions.length).toEqual(1);
|
|
320
|
+
expect(latestReaction).toMatchObject(newReaction);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the comment reaction deletion', () => {
|
|
324
|
+
const otherUserPayload = generateCommentReactionUpdatedEvent({
|
|
325
|
+
comment: existingComment,
|
|
326
|
+
reaction: {
|
|
327
|
+
...existingReaction,
|
|
328
|
+
user: {
|
|
329
|
+
id: getHumanId(),
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
handleCommentReactionUpdated.call(feed, otherUserPayload);
|
|
335
|
+
|
|
336
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
337
|
+
|
|
338
|
+
handleCommentReactionUpdated.call(feed, otherUserPayload);
|
|
339
|
+
|
|
340
|
+
const comments =
|
|
341
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
342
|
+
const comment = comments?.find((a) => a.id === commentId);
|
|
343
|
+
const [latestReaction] = comment?.own_reactions ?? [];
|
|
344
|
+
|
|
345
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
346
|
+
expect(comment?.own_reactions.length).toEqual(1);
|
|
347
|
+
expect(latestReaction).toMatchObject(existingReaction);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Feed } from '../../feed';
|
|
2
|
+
import type { EventPayload } from '../../../types-internal';
|
|
3
|
+
import { type PartializeAllBut } from '../../../types-internal';
|
|
4
|
+
import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
|
|
5
|
+
|
|
6
|
+
export type CommentReactionUpdatedPayload = PartializeAllBut<
|
|
7
|
+
EventPayload<'feeds.comment.reaction.updated'>,
|
|
8
|
+
'comment' | 'reaction'
|
|
9
|
+
>;
|
|
10
|
+
|
|
11
|
+
export function handleCommentReactionUpdated(
|
|
12
|
+
this: Feed,
|
|
13
|
+
payload: CommentReactionUpdatedPayload,
|
|
14
|
+
fromWs?: boolean,
|
|
15
|
+
) {
|
|
16
|
+
const { comment, reaction } = payload;
|
|
17
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
18
|
+
|
|
19
|
+
const isOwnReaction = reaction.user.id === connectedUser?.id;
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
!shouldUpdateState({
|
|
23
|
+
stateUpdateQueueId: getStateUpdateQueueId(
|
|
24
|
+
payload,
|
|
25
|
+
'comment-reaction-updated',
|
|
26
|
+
),
|
|
27
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
28
|
+
watch: this.currentState.watch,
|
|
29
|
+
fromWs,
|
|
30
|
+
isTriggeredByConnectedUser: isOwnReaction,
|
|
31
|
+
})
|
|
32
|
+
) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.state.next((currentState) => {
|
|
37
|
+
const commentIndex = this.getCommentIndex(comment, currentState);
|
|
38
|
+
|
|
39
|
+
if (commentIndex === -1) return currentState;
|
|
40
|
+
|
|
41
|
+
const forId = comment.parent_id ?? comment.object_id;
|
|
42
|
+
|
|
43
|
+
const entityState = currentState.comments_by_entity_id[forId];
|
|
44
|
+
|
|
45
|
+
const newComments = entityState?.comments?.concat([]) ?? [];
|
|
46
|
+
|
|
47
|
+
let ownReactions = newComments[commentIndex].own_reactions;
|
|
48
|
+
|
|
49
|
+
if (isOwnReaction) {
|
|
50
|
+
ownReactions = [reaction];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
newComments[commentIndex] = {
|
|
54
|
+
...newComments[commentIndex],
|
|
55
|
+
reaction_count: comment.reaction_count ?? 0,
|
|
56
|
+
latest_reactions: comment.latest_reactions ?? [],
|
|
57
|
+
reaction_groups: comment.reaction_groups ?? {},
|
|
58
|
+
own_reactions: ownReactions,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
...currentState,
|
|
63
|
+
comments_by_entity_id: {
|
|
64
|
+
...currentState.comments_by_entity_id,
|
|
65
|
+
[forId]: {
|
|
66
|
+
...entityState,
|
|
67
|
+
comments: newComments,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -3,4 +3,4 @@ export * from './handle-comment-deleted';
|
|
|
3
3
|
export * from './handle-comment-updated';
|
|
4
4
|
export * from './handle-comment-reaction-added';
|
|
5
5
|
export * from './handle-comment-reaction-deleted';
|
|
6
|
-
|
|
6
|
+
export * from './handle-comment-reaction-updated';
|
package/src/feed/feed.ts
CHANGED
|
@@ -42,8 +42,10 @@ import {
|
|
|
42
42
|
handleActivityMarked,
|
|
43
43
|
handleActivityReactionAdded,
|
|
44
44
|
handleActivityReactionDeleted,
|
|
45
|
+
handleActivityReactionUpdated,
|
|
45
46
|
handleCommentReactionAdded,
|
|
46
47
|
handleCommentReactionDeleted,
|
|
48
|
+
handleCommentReactionUpdated,
|
|
47
49
|
addAggregatedActivitiesToState,
|
|
48
50
|
updateNotificationStatus,
|
|
49
51
|
handleStoriesFeedUpdated,
|
|
@@ -151,7 +153,7 @@ export class Feed extends FeedApi {
|
|
|
151
153
|
'feeds.activity.deleted': handleActivityDeleted.bind(this),
|
|
152
154
|
'feeds.activity.reaction.added': handleActivityReactionAdded.bind(this),
|
|
153
155
|
'feeds.activity.reaction.deleted': handleActivityReactionDeleted.bind(this),
|
|
154
|
-
'feeds.activity.reaction.updated':
|
|
156
|
+
'feeds.activity.reaction.updated': handleActivityReactionUpdated.bind(this),
|
|
155
157
|
'feeds.activity.removed_from_feed':
|
|
156
158
|
handleActivityRemovedFromFeed.bind(this),
|
|
157
159
|
'feeds.activity.updated': handleActivityUpdated.bind(this),
|
|
@@ -173,7 +175,7 @@ export class Feed extends FeedApi {
|
|
|
173
175
|
'feeds.follow.updated': handleFollowUpdated.bind(this),
|
|
174
176
|
'feeds.comment.reaction.added': handleCommentReactionAdded.bind(this),
|
|
175
177
|
'feeds.comment.reaction.deleted': handleCommentReactionDeleted.bind(this),
|
|
176
|
-
'feeds.comment.reaction.updated':
|
|
178
|
+
'feeds.comment.reaction.updated': handleCommentReactionUpdated.bind(this),
|
|
177
179
|
'feeds.feed_member.added': handleFeedMemberAdded.bind(this),
|
|
178
180
|
'feeds.feed_member.removed': handleFeedMemberRemoved.bind(this),
|
|
179
181
|
'feeds.feed_member.updated': handleFeedMemberUpdated.bind(this),
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
Feed,
|
|
52
52
|
handleActivityReactionAdded,
|
|
53
53
|
handleActivityReactionDeleted,
|
|
54
|
+
handleActivityReactionUpdated,
|
|
54
55
|
handleActivityUpdated,
|
|
55
56
|
handleCommentAdded,
|
|
56
57
|
handleCommentDeleted,
|
|
@@ -68,7 +69,8 @@ import { handleUserUpdated } from './event-handlers';
|
|
|
68
69
|
import type { SyncFailure } from '../common/real-time/event-models';
|
|
69
70
|
import { UnhandledErrorType } from '../common/real-time/event-models';
|
|
70
71
|
import { updateCommentCount } from '../feed/event-handlers/comment/utils';
|
|
71
|
-
import { configureLoggers } from '../utils
|
|
72
|
+
import { configureLoggers } from '../utils';
|
|
73
|
+
import { handleCommentReactionUpdated } from '../feed/event-handlers/comment/handle-comment-reaction-updated';
|
|
72
74
|
|
|
73
75
|
export type FeedsClientState = {
|
|
74
76
|
connected_user: OwnUser | undefined;
|
|
@@ -417,9 +419,14 @@ export class FeedsClient extends FeedsApi {
|
|
|
417
419
|
activity_id: string;
|
|
418
420
|
},
|
|
419
421
|
) => {
|
|
422
|
+
const shouldEnforceUnique = request.enforce_unique;
|
|
420
423
|
const response = await super.addActivityReaction(request);
|
|
421
424
|
for (const feed of Object.values(this.activeFeeds)) {
|
|
422
|
-
|
|
425
|
+
if (shouldEnforceUnique) {
|
|
426
|
+
handleActivityReactionUpdated.bind(feed)(response, false);
|
|
427
|
+
} else {
|
|
428
|
+
handleActivityReactionAdded.bind(feed)(response, false);
|
|
429
|
+
}
|
|
423
430
|
}
|
|
424
431
|
return response;
|
|
425
432
|
};
|
|
@@ -449,9 +456,14 @@ export class FeedsClient extends FeedsApi {
|
|
|
449
456
|
addCommentReaction = async (
|
|
450
457
|
request: AddCommentReactionRequest & { id: string },
|
|
451
458
|
): Promise<StreamResponse<AddCommentReactionResponse>> => {
|
|
459
|
+
const shouldEnforceUnique = request.enforce_unique;
|
|
452
460
|
const response = await super.addCommentReaction(request);
|
|
453
461
|
for (const feed of Object.values(this.activeFeeds)) {
|
|
454
|
-
|
|
462
|
+
if (shouldEnforceUnique) {
|
|
463
|
+
handleCommentReactionUpdated.bind(feed)(response, false);
|
|
464
|
+
} else {
|
|
465
|
+
handleCommentReactionAdded.bind(feed)(response, false);
|
|
466
|
+
}
|
|
455
467
|
}
|
|
456
468
|
return response;
|
|
457
469
|
};
|
|
@@ -59,6 +59,8 @@ import type {
|
|
|
59
59
|
ListBlockListResponse,
|
|
60
60
|
ListDevicesResponse,
|
|
61
61
|
MarkActivityRequest,
|
|
62
|
+
OwnCapabilitiesBatchRequest,
|
|
63
|
+
OwnCapabilitiesBatchResponse,
|
|
62
64
|
PinActivityRequest,
|
|
63
65
|
PinActivityResponse,
|
|
64
66
|
PollOptionResponse,
|
|
@@ -1523,6 +1525,32 @@ export class FeedsApi {
|
|
|
1523
1525
|
return { ...response.body, metadata: response.metadata };
|
|
1524
1526
|
}
|
|
1525
1527
|
|
|
1528
|
+
async ownCapabilitiesBatch(
|
|
1529
|
+
request: OwnCapabilitiesBatchRequest & { connection_id?: string },
|
|
1530
|
+
): Promise<StreamResponse<OwnCapabilitiesBatchResponse>> {
|
|
1531
|
+
const queryParams = {
|
|
1532
|
+
connection_id: request?.connection_id,
|
|
1533
|
+
};
|
|
1534
|
+
const body = {
|
|
1535
|
+
feeds: request?.feeds,
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1538
|
+
const response = await this.apiClient.sendRequest<
|
|
1539
|
+
StreamResponse<OwnCapabilitiesBatchResponse>
|
|
1540
|
+
>(
|
|
1541
|
+
'POST',
|
|
1542
|
+
'/api/v2/feeds/feeds/own_capabilities/batch',
|
|
1543
|
+
undefined,
|
|
1544
|
+
queryParams,
|
|
1545
|
+
body,
|
|
1546
|
+
'application/json',
|
|
1547
|
+
);
|
|
1548
|
+
|
|
1549
|
+
decoders.OwnCapabilitiesBatchResponse?.(response.body);
|
|
1550
|
+
|
|
1551
|
+
return { ...response.body, metadata: response.metadata };
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1526
1554
|
protected async _queryFeeds(
|
|
1527
1555
|
request?: QueryFeedsRequest & { connection_id?: string },
|
|
1528
1556
|
): Promise<StreamResponse<QueryFeedsResponse>> {
|
package/src/gen/models/index.ts
CHANGED
|
@@ -4177,6 +4177,16 @@ export interface OnlyUserID {
|
|
|
4177
4177
|
id: string;
|
|
4178
4178
|
}
|
|
4179
4179
|
|
|
4180
|
+
export interface OwnCapabilitiesBatchRequest {
|
|
4181
|
+
feeds: string[];
|
|
4182
|
+
}
|
|
4183
|
+
|
|
4184
|
+
export interface OwnCapabilitiesBatchResponse {
|
|
4185
|
+
duration: string;
|
|
4186
|
+
|
|
4187
|
+
capabilities: Record<string, FeedOwnCapability[]>;
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4180
4190
|
export interface OwnUser {
|
|
4181
4191
|
banned: boolean;
|
|
4182
4192
|
|
|
@@ -283,6 +283,32 @@ export function generateActivityReactionAddedEvent(
|
|
|
283
283
|
};
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
export function generateActivityReactionUpdatedEvent(
|
|
287
|
+
overrides: Omit<
|
|
288
|
+
Partial<EventPayload<'feeds.activity.reaction.updated'>>,
|
|
289
|
+
'activity' | 'type' | 'reaction' | 'user'
|
|
290
|
+
> & {
|
|
291
|
+
activity?: Parameters<typeof generateActivityResponse>[0];
|
|
292
|
+
reaction?: Parameters<typeof generateFeedReactionResponse>[0];
|
|
293
|
+
user?: Parameters<typeof generateUserResponse>[0];
|
|
294
|
+
} = {},
|
|
295
|
+
): EventPayload<'feeds.activity.reaction.updated'> {
|
|
296
|
+
const activity = generateActivityResponse(overrides.activity);
|
|
297
|
+
const reaction = generateFeedReactionResponse(overrides.reaction);
|
|
298
|
+
const user = generateUserResponse(overrides.user);
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
type: 'feeds.activity.reaction.updated',
|
|
302
|
+
created_at: new Date(),
|
|
303
|
+
fid: '',
|
|
304
|
+
custom: {},
|
|
305
|
+
...overrides,
|
|
306
|
+
user,
|
|
307
|
+
reaction,
|
|
308
|
+
activity,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
286
312
|
export function generateActivityReactionDeletedEvent(
|
|
287
313
|
overrides: Omit<
|
|
288
314
|
Partial<EventPayload<'feeds.activity.reaction.deleted'>>,
|
|
@@ -532,6 +558,32 @@ export function generateCommentReactionDeletedEvent(
|
|
|
532
558
|
};
|
|
533
559
|
}
|
|
534
560
|
|
|
561
|
+
export function generateCommentReactionUpdatedEvent(
|
|
562
|
+
overrides: Omit<
|
|
563
|
+
Partial<EventPayload<'feeds.comment.reaction.updated'>>,
|
|
564
|
+
'comment' | 'reaction' | 'activity' | 'type'
|
|
565
|
+
> & {
|
|
566
|
+
comment?: Parameters<typeof generateCommentResponse>[0];
|
|
567
|
+
reaction?: Parameters<typeof generateFeedReactionResponse>[0];
|
|
568
|
+
activity?: Parameters<typeof generateActivityResponse>[0];
|
|
569
|
+
} = {},
|
|
570
|
+
): EventPayload<'feeds.comment.reaction.updated'> {
|
|
571
|
+
const comment = generateCommentResponse(overrides.comment);
|
|
572
|
+
const reaction = generateFeedReactionResponse(overrides.reaction);
|
|
573
|
+
const activity = generateActivityResponse(overrides.activity);
|
|
574
|
+
|
|
575
|
+
return {
|
|
576
|
+
type: 'feeds.comment.reaction.updated',
|
|
577
|
+
created_at: new Date(),
|
|
578
|
+
fid: '',
|
|
579
|
+
custom: {},
|
|
580
|
+
...overrides,
|
|
581
|
+
comment,
|
|
582
|
+
reaction,
|
|
583
|
+
activity,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
535
587
|
export function generateFeedMemberAddedEvent(
|
|
536
588
|
overrides: Omit<
|
|
537
589
|
Partial<EventPayload<'feeds.feed_member.added'>>,
|
|
@@ -12,13 +12,21 @@ import type {
|
|
|
12
12
|
CommentUpdatedPayload,
|
|
13
13
|
} from '../feed';
|
|
14
14
|
import { ensureExhausted } from './ensure-exhausted';
|
|
15
|
+
import type {
|
|
16
|
+
CommentReactionUpdatedPayload
|
|
17
|
+
} from '../feed/event-handlers/comment/handle-comment-reaction-updated';
|
|
18
|
+
import type {
|
|
19
|
+
ActivityReactionUpdatedPayload
|
|
20
|
+
} from '../feed/event-handlers/activity/handle-activity-reaction-updated';
|
|
15
21
|
|
|
16
22
|
export type StateUpdateQueuePrefix =
|
|
17
23
|
| 'activity-updated'
|
|
18
24
|
| 'activity-reaction-created'
|
|
19
25
|
| 'activity-reaction-deleted'
|
|
26
|
+
| 'activity-reaction-updated'
|
|
20
27
|
| 'comment-reaction-created'
|
|
21
28
|
| 'comment-reaction-deleted'
|
|
29
|
+
| 'comment-reaction-updated'
|
|
22
30
|
| 'follow-created'
|
|
23
31
|
| 'follow-deleted'
|
|
24
32
|
| 'follow-updated'
|
|
@@ -30,8 +38,10 @@ type StateUpdateQueuePayloadByPrefix = {
|
|
|
30
38
|
'activity-updated': ActivityUpdatedPayload;
|
|
31
39
|
'activity-reaction-created': ActivityReactionAddedPayload;
|
|
32
40
|
'activity-reaction-deleted': ActivityReactionDeletedPayload;
|
|
41
|
+
'activity-reaction-updated': ActivityReactionUpdatedPayload;
|
|
33
42
|
'comment-reaction-created': CommentReactionAddedPayload;
|
|
34
43
|
'comment-reaction-deleted': CommentReactionDeletedPayload;
|
|
44
|
+
'comment-reaction-updated': CommentReactionUpdatedPayload;
|
|
35
45
|
'follow-created': FollowResponse;
|
|
36
46
|
'follow-deleted': FollowResponse;
|
|
37
47
|
'follow-updated': FollowResponse;
|
|
@@ -163,7 +173,8 @@ export function getStateUpdateQueueId(
|
|
|
163
173
|
return toJoin.concat([data.activity.id]).join('-')
|
|
164
174
|
}
|
|
165
175
|
case 'activity-reaction-created':
|
|
166
|
-
case 'activity-reaction-deleted':
|
|
176
|
+
case 'activity-reaction-deleted':
|
|
177
|
+
case 'activity-reaction-updated': {
|
|
167
178
|
return toJoin
|
|
168
179
|
.concat([
|
|
169
180
|
data.activity.id,
|
|
@@ -172,7 +183,8 @@ export function getStateUpdateQueueId(
|
|
|
172
183
|
.join('-');
|
|
173
184
|
}
|
|
174
185
|
case 'comment-reaction-created':
|
|
175
|
-
case 'comment-reaction-deleted':
|
|
186
|
+
case 'comment-reaction-deleted':
|
|
187
|
+
case 'comment-reaction-updated': {
|
|
176
188
|
return toJoin
|
|
177
189
|
.concat([
|
|
178
190
|
data.comment.id,
|