@stream-io/feeds-client 0.2.16 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/index.js +1 -1
  3. package/dist/cjs/react-bindings.js +1 -1
  4. package/dist/es/index.mjs +2 -2
  5. package/dist/es/react-bindings.mjs +1 -1
  6. package/dist/{index-CaFrpjpl.js → index--koeDtxd.js} +310 -80
  7. package/dist/index--koeDtxd.js.map +1 -0
  8. package/dist/{index-J3MkoYPN.mjs → index-Zde8UE5f.mjs} +310 -80
  9. package/dist/index-Zde8UE5f.mjs.map +1 -0
  10. package/dist/tsconfig.tsbuildinfo +1 -1
  11. package/dist/types/common/real-time/StableWSConnection.d.ts +3 -3
  12. package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts +4 -3
  13. package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts.map +1 -1
  14. package/dist/types/feed/event-handlers/activity/handle-activity-reaction-updated.d.ts +14 -0
  15. package/dist/types/feed/event-handlers/activity/handle-activity-reaction-updated.d.ts.map +1 -0
  16. package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts.map +1 -1
  17. package/dist/types/feed/event-handlers/activity/index.d.ts +1 -0
  18. package/dist/types/feed/event-handlers/activity/index.d.ts.map +1 -1
  19. package/dist/types/feed/event-handlers/activity-updater.d.ts +44 -0
  20. package/dist/types/feed/event-handlers/activity-updater.d.ts.map +1 -0
  21. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts +6 -0
  22. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts.map +1 -0
  23. package/dist/types/feed/event-handlers/comment/handle-comment-reaction-added.d.ts.map +1 -1
  24. package/dist/types/feed/event-handlers/comment/handle-comment-reaction-updated.d.ts +6 -0
  25. package/dist/types/feed/event-handlers/comment/handle-comment-reaction-updated.d.ts.map +1 -0
  26. package/dist/types/feed/event-handlers/comment/index.d.ts +1 -0
  27. package/dist/types/feed/event-handlers/comment/index.d.ts.map +1 -1
  28. package/dist/types/feed/event-handlers/index.d.ts +3 -1
  29. package/dist/types/feed/event-handlers/index.d.ts.map +1 -1
  30. package/dist/types/feed/event-handlers/{aggregated-feed/handle-aggregated-feed-updated.d.ts → notification-feed/handle-notification-feed-updated.d.ts} +2 -11
  31. package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map +1 -0
  32. package/dist/types/feed/event-handlers/notification-feed/index.d.ts +2 -0
  33. package/dist/types/feed/event-handlers/notification-feed/index.d.ts.map +1 -0
  34. package/dist/types/feed/event-handlers/story-feeds/handle-story-feeds-updated.d.ts +15 -0
  35. package/dist/types/feed/event-handlers/story-feeds/handle-story-feeds-updated.d.ts.map +1 -0
  36. package/dist/types/feed/event-handlers/story-feeds/index.d.ts +2 -0
  37. package/dist/types/feed/event-handlers/story-feeds/index.d.ts.map +1 -0
  38. package/dist/types/feed/feed.d.ts +9 -3
  39. package/dist/types/feed/feed.d.ts.map +1 -1
  40. package/dist/types/feeds-client/feeds-client.d.ts +5 -3
  41. package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
  42. package/dist/types/gen/feeds/FeedsApi.d.ts.map +1 -1
  43. package/dist/types/gen/models/index.d.ts +43 -452
  44. package/dist/types/gen/models/index.d.ts.map +1 -1
  45. package/dist/types/utils/state-update-queue.d.ts +5 -1
  46. package/dist/types/utils/state-update-queue.d.ts.map +1 -1
  47. package/package.json +1 -1
  48. package/src/feed/event-handlers/activity/handle-activity-added.test.ts +16 -5
  49. package/src/feed/event-handlers/activity/handle-activity-added.ts +9 -11
  50. package/src/feed/event-handlers/activity/handle-activity-reaction-updated.test.ts +282 -0
  51. package/src/feed/event-handlers/activity/handle-activity-reaction-updated.ts +140 -0
  52. package/src/feed/event-handlers/activity/handle-activity-updated.ts +8 -16
  53. package/src/feed/event-handlers/activity/index.ts +1 -0
  54. package/src/feed/event-handlers/activity-updater.ts +15 -0
  55. package/src/feed/event-handlers/add-aggregated-activities-to-state.test.ts +510 -0
  56. package/src/feed/event-handlers/add-aggregated-activities-to-state.ts +72 -0
  57. package/src/feed/event-handlers/comment/handle-comment-reaction-added.ts +1 -2
  58. package/src/feed/event-handlers/comment/handle-comment-reaction-updated.test.ts +350 -0
  59. package/src/feed/event-handlers/comment/handle-comment-reaction-updated.ts +72 -0
  60. package/src/feed/event-handlers/comment/index.ts +1 -1
  61. package/src/feed/event-handlers/index.ts +3 -1
  62. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.test.ts +182 -0
  63. package/src/feed/event-handlers/{aggregated-feed/handle-aggregated-feed-updated.ts → notification-feed/handle-notification-feed-updated.ts} +2 -94
  64. package/src/feed/event-handlers/notification-feed/index.ts +1 -0
  65. package/src/feed/event-handlers/story-feeds/handle-story-feeds-updated.test.ts +45 -0
  66. package/src/feed/event-handlers/story-feeds/handle-story-feeds-updated.ts +122 -0
  67. package/src/feed/event-handlers/story-feeds/index.ts +1 -0
  68. package/src/feed/feed.ts +16 -2
  69. package/src/feeds-client/feeds-client.ts +36 -6
  70. package/src/gen/feeds/FeedsApi.ts +5 -0
  71. package/src/gen/model-decoders/decoders.ts +10 -4
  72. package/src/gen/models/index.ts +76 -835
  73. package/src/test-utils/response-generators.ts +89 -1
  74. package/src/utils/state-update-queue.ts +14 -2
  75. package/dist/index-CaFrpjpl.js.map +0 -1
  76. package/dist/index-J3MkoYPN.mjs.map +0 -1
  77. package/dist/types/feed/event-handlers/aggregated-feed/handle-aggregated-feed-updated.d.ts.map +0 -1
  78. package/dist/types/feed/event-handlers/aggregated-feed/index.d.ts +0 -2
  79. package/dist/types/feed/event-handlers/aggregated-feed/index.d.ts.map +0 -1
  80. package/src/feed/event-handlers/activity/activity-utils.test.ts +0 -252
  81. package/src/feed/event-handlers/aggregated-feed/handle-aggregated-feed-updated.test.ts +0 -644
  82. package/src/feed/event-handlers/aggregated-feed/index.ts +0 -1
@@ -0,0 +1,510 @@
1
+ import {
2
+ createMockAggregatedActivity,
3
+ generateActivityResponse,
4
+ generateFeedReactionResponse,
5
+ } from '../../test-utils';
6
+ import { addAggregatedActivitiesToState } from './add-aggregated-activities-to-state';
7
+ import { describe, expect, it } from 'vitest';
8
+
9
+ describe('addAggregatedActivitiesToState', () => {
10
+ it('should add new activities when none exist', () => {
11
+ const newActivities = [
12
+ createMockAggregatedActivity({ group: 'group1' }),
13
+ createMockAggregatedActivity({ group: 'group2' }),
14
+ ];
15
+
16
+ const result = addAggregatedActivitiesToState(
17
+ newActivities,
18
+ undefined,
19
+ 'start',
20
+ );
21
+
22
+ expect(result.changed).toBe(true);
23
+ expect(result.aggregated_activities).toStrictEqual(newActivities);
24
+ });
25
+
26
+ it('should add new activities to existing ones', () => {
27
+ const existingActivities = [
28
+ createMockAggregatedActivity({ group: 'existing1' }),
29
+ ];
30
+ const newActivities = [
31
+ createMockAggregatedActivity({ group: 'new1' }),
32
+ createMockAggregatedActivity({ group: 'new2' }),
33
+ ];
34
+
35
+ const result = addAggregatedActivitiesToState(
36
+ newActivities,
37
+ existingActivities,
38
+ 'start',
39
+ );
40
+
41
+ expect(result.changed).toBe(true);
42
+ expect(result.aggregated_activities).toStrictEqual([
43
+ ...newActivities,
44
+ ...existingActivities,
45
+ ]);
46
+ });
47
+
48
+ it('should add new activities at the end when position is end', () => {
49
+ const existingActivities = [
50
+ createMockAggregatedActivity({ group: 'existing1' }),
51
+ ];
52
+ const newActivities = [createMockAggregatedActivity({ group: 'new1' })];
53
+
54
+ const result = addAggregatedActivitiesToState(
55
+ newActivities,
56
+ existingActivities,
57
+ 'end',
58
+ );
59
+
60
+ expect(result.changed).toBe(true);
61
+ expect(result.aggregated_activities).toStrictEqual([
62
+ ...existingActivities,
63
+ ...newActivities,
64
+ ]);
65
+ });
66
+
67
+ it('should update existing activities with same group (upsert)', () => {
68
+ const baseDate = new Date('2023-01-01');
69
+ const existingActivities = [
70
+ createMockAggregatedActivity({
71
+ group: 'group1',
72
+ activity_count: 1,
73
+ score: 10,
74
+ updated_at: baseDate,
75
+ }),
76
+ createMockAggregatedActivity({
77
+ group: 'group2',
78
+ activity_count: 2,
79
+ score: 20,
80
+ }),
81
+ ];
82
+ const newActivities = [
83
+ createMockAggregatedActivity({
84
+ group: 'group1',
85
+ activity_count: 3,
86
+ score: 30,
87
+ updated_at: new Date('2023-01-02'),
88
+ }),
89
+ createMockAggregatedActivity({
90
+ group: 'group3',
91
+ activity_count: 4,
92
+ score: 40,
93
+ }),
94
+ ];
95
+
96
+ const result = addAggregatedActivitiesToState(
97
+ newActivities,
98
+ existingActivities,
99
+ 'start',
100
+ );
101
+
102
+ expect(result.changed).toBe(true);
103
+ expect(result.aggregated_activities).toHaveLength(3);
104
+
105
+ // Check that group1 was updated
106
+ const updatedGroup1 = result.aggregated_activities.find(
107
+ (a) => a.group === 'group1',
108
+ );
109
+ expect(updatedGroup1?.activity_count).toBe(3);
110
+ expect(updatedGroup1?.score).toBe(30);
111
+ expect(updatedGroup1?.updated_at).toEqual(new Date('2023-01-02'));
112
+
113
+ // Check that group2 remains unchanged
114
+ const unchangedGroup2 = result.aggregated_activities.find(
115
+ (a) => a.group === 'group2',
116
+ );
117
+ expect(unchangedGroup2?.activity_count).toBe(2);
118
+ expect(unchangedGroup2?.score).toBe(20);
119
+
120
+ // Check that group3 was added
121
+ const newGroup3 = result.aggregated_activities.find(
122
+ (a) => a.group === 'group3',
123
+ );
124
+ expect(newGroup3?.activity_count).toBe(4);
125
+ expect(newGroup3?.score).toBe(40);
126
+ });
127
+
128
+ it('should handle mixed new and existing activities', () => {
129
+ const existingActivities = [
130
+ createMockAggregatedActivity({ group: 'existing1' }),
131
+ createMockAggregatedActivity({ group: 'existing2' }),
132
+ ];
133
+ const newActivities = [
134
+ createMockAggregatedActivity({ group: 'existing1', activity_count: 5 }), // Update existing
135
+ createMockAggregatedActivity({ group: 'new1' }), // Add new
136
+ createMockAggregatedActivity({ group: 'existing2', score: 100 }), // Update existing
137
+ createMockAggregatedActivity({ group: 'new2' }), // Add new
138
+ ];
139
+
140
+ const result = addAggregatedActivitiesToState(
141
+ newActivities,
142
+ existingActivities,
143
+ 'start',
144
+ );
145
+
146
+ expect(result.changed).toBe(true);
147
+ expect(result.aggregated_activities).toHaveLength(4);
148
+
149
+ // Check that existing1 was updated
150
+ const updatedExisting1 = result.aggregated_activities.find(
151
+ (a) => a.group === 'existing1',
152
+ );
153
+ expect(updatedExisting1?.activity_count).toBe(5);
154
+
155
+ // Check that existing2 was updated
156
+ const updatedExisting2 = result.aggregated_activities.find(
157
+ (a) => a.group === 'existing2',
158
+ );
159
+ expect(updatedExisting2?.score).toBe(100);
160
+
161
+ // Check that new activities were added
162
+ expect(
163
+ result.aggregated_activities.find((a) => a.group === 'new1'),
164
+ ).toBeDefined();
165
+ expect(
166
+ result.aggregated_activities.find((a) => a.group === 'new2'),
167
+ ).toBeDefined();
168
+ });
169
+
170
+ it('should preserve order when adding at start', () => {
171
+ const existingActivities = [
172
+ createMockAggregatedActivity({ group: 'existing1' }),
173
+ createMockAggregatedActivity({ group: 'existing2' }),
174
+ ];
175
+ const newActivities = [
176
+ createMockAggregatedActivity({ group: 'new1' }),
177
+ createMockAggregatedActivity({ group: 'new2' }),
178
+ ];
179
+
180
+ const result = addAggregatedActivitiesToState(
181
+ newActivities,
182
+ existingActivities,
183
+ 'start',
184
+ );
185
+
186
+ expect(result.aggregated_activities).toStrictEqual([
187
+ ...newActivities,
188
+ ...existingActivities,
189
+ ]);
190
+ });
191
+
192
+ it('should preserve order when adding at end', () => {
193
+ const existingActivities = [
194
+ createMockAggregatedActivity({ group: 'existing1' }),
195
+ createMockAggregatedActivity({ group: 'existing2' }),
196
+ ];
197
+ const newActivities = [
198
+ createMockAggregatedActivity({ group: 'new1' }),
199
+ createMockAggregatedActivity({ group: 'new2' }),
200
+ ];
201
+
202
+ const result = addAggregatedActivitiesToState(
203
+ newActivities,
204
+ existingActivities,
205
+ 'end',
206
+ );
207
+
208
+ expect(result.aggregated_activities).toStrictEqual([
209
+ ...existingActivities,
210
+ ...newActivities,
211
+ ]);
212
+ });
213
+
214
+ describe('replace position', () => {
215
+ it('should replace existing activities with same group', () => {
216
+ const baseDate = new Date('2023-01-01');
217
+ const existingActivities = [
218
+ createMockAggregatedActivity({
219
+ group: 'group1',
220
+ activity_count: 1,
221
+ score: 10,
222
+ updated_at: baseDate,
223
+ }),
224
+ createMockAggregatedActivity({
225
+ group: 'group2',
226
+ activity_count: 2,
227
+ score: 20,
228
+ updated_at: baseDate,
229
+ }),
230
+ ];
231
+ const newActivities = [
232
+ createMockAggregatedActivity({
233
+ group: 'group1',
234
+ activity_count: 3,
235
+ score: 30,
236
+ updated_at: new Date('2023-01-02'),
237
+ }),
238
+ createMockAggregatedActivity({
239
+ group: 'group3',
240
+ activity_count: 4,
241
+ score: 40,
242
+ updated_at: new Date('2023-01-02'),
243
+ }),
244
+ ];
245
+
246
+ const result = addAggregatedActivitiesToState(
247
+ newActivities,
248
+ existingActivities,
249
+ 'replace',
250
+ );
251
+
252
+ expect(result.changed).toBe(true);
253
+ expect(result.aggregated_activities).toHaveLength(3);
254
+
255
+ // Check that group1 was replaced
256
+ const replacedGroup1 = result.aggregated_activities.find(
257
+ (a) => a.group === 'group1',
258
+ );
259
+ expect(replacedGroup1?.activity_count).toBe(3);
260
+ expect(replacedGroup1?.score).toBe(30);
261
+ expect(replacedGroup1?.updated_at).toEqual(new Date('2023-01-02'));
262
+
263
+ // Check that group2 remains unchanged
264
+ const unchangedGroup2 = result.aggregated_activities.find(
265
+ (a) => a.group === 'group2',
266
+ );
267
+ expect(unchangedGroup2?.activity_count).toBe(2);
268
+ expect(unchangedGroup2?.score).toBe(20);
269
+ expect(unchangedGroup2?.updated_at).toEqual(baseDate);
270
+
271
+ // Check that group3 was added
272
+ const newGroup3 = result.aggregated_activities.find(
273
+ (a) => a.group === 'group3',
274
+ );
275
+ expect(newGroup3?.activity_count).toBe(4);
276
+ expect(newGroup3?.score).toBe(40);
277
+ expect(newGroup3?.updated_at).toEqual(new Date('2023-01-02'));
278
+ });
279
+
280
+ it('should preserve order of existing activities when replacing', () => {
281
+ const existingActivities = [
282
+ createMockAggregatedActivity({ group: 'group1', activity_count: 1 }),
283
+ createMockAggregatedActivity({ group: 'group2', activity_count: 2 }),
284
+ createMockAggregatedActivity({ group: 'group3', activity_count: 3 }),
285
+ ];
286
+ const newActivities = [
287
+ createMockAggregatedActivity({ group: 'group2', activity_count: 20 }),
288
+ createMockAggregatedActivity({ group: 'group1', activity_count: 10 }),
289
+ ];
290
+
291
+ const result = addAggregatedActivitiesToState(
292
+ newActivities,
293
+ existingActivities,
294
+ 'replace',
295
+ );
296
+
297
+ expect(result.changed).toBe(true);
298
+ expect(result.aggregated_activities).toHaveLength(3);
299
+
300
+ // Check that order is preserved
301
+ expect(result.aggregated_activities[0].group).toBe('group1');
302
+ expect(result.aggregated_activities[0].activity_count).toBe(10);
303
+ expect(result.aggregated_activities[1].group).toBe('group2');
304
+ expect(result.aggregated_activities[1].activity_count).toBe(20);
305
+ expect(result.aggregated_activities[2].group).toBe('group3');
306
+ expect(result.aggregated_activities[2].activity_count).toBe(3);
307
+ });
308
+
309
+ it('should add new activities at the end when replacing', () => {
310
+ const existingActivities = [
311
+ createMockAggregatedActivity({ group: 'group1', activity_count: 1 }),
312
+ createMockAggregatedActivity({ group: 'group2', activity_count: 2 }),
313
+ ];
314
+ const newActivities = [
315
+ createMockAggregatedActivity({ group: 'group1', activity_count: 10 }),
316
+ createMockAggregatedActivity({ group: 'group3', activity_count: 3 }),
317
+ createMockAggregatedActivity({ group: 'group4', activity_count: 4 }),
318
+ ];
319
+
320
+ const result = addAggregatedActivitiesToState(
321
+ newActivities,
322
+ existingActivities,
323
+ 'replace',
324
+ );
325
+
326
+ expect(result.changed).toBe(true);
327
+ expect(result.aggregated_activities).toHaveLength(4);
328
+
329
+ // Check that existing activities are in their original positions
330
+ expect(result.aggregated_activities[0].group).toBe('group1');
331
+ expect(result.aggregated_activities[0].activity_count).toBe(10);
332
+ expect(result.aggregated_activities[1].group).toBe('group2');
333
+ expect(result.aggregated_activities[1].activity_count).toBe(2);
334
+
335
+ // Check that new activities are added at the end
336
+ expect(result.aggregated_activities[2].group).toBe('group3');
337
+ expect(result.aggregated_activities[2].activity_count).toBe(3);
338
+ expect(result.aggregated_activities[3].group).toBe('group4');
339
+ expect(result.aggregated_activities[3].activity_count).toBe(4);
340
+ });
341
+
342
+ it('should handle empty new activities', () => {
343
+ const existingActivities = [
344
+ createMockAggregatedActivity({ group: 'group1', activity_count: 1 }),
345
+ createMockAggregatedActivity({ group: 'group2', activity_count: 2 }),
346
+ ];
347
+
348
+ const result = addAggregatedActivitiesToState(
349
+ [],
350
+ existingActivities,
351
+ 'replace',
352
+ );
353
+
354
+ expect(result.changed).toBe(false);
355
+ expect(result.aggregated_activities).toStrictEqual(existingActivities);
356
+ });
357
+
358
+ it('should handle both arrays being empty', () => {
359
+ const result = addAggregatedActivitiesToState([], [], 'replace');
360
+
361
+ expect(result.changed).toBe(false);
362
+ expect(result.aggregated_activities).toStrictEqual([]);
363
+ });
364
+
365
+ it('should replace multiple activities with same groups', () => {
366
+ const existingActivities = [
367
+ createMockAggregatedActivity({
368
+ group: 'group1',
369
+ activity_count: 1,
370
+ score: 10,
371
+ }),
372
+ createMockAggregatedActivity({
373
+ group: 'group2',
374
+ activity_count: 2,
375
+ score: 20,
376
+ }),
377
+ createMockAggregatedActivity({
378
+ group: 'group3',
379
+ activity_count: 3,
380
+ score: 30,
381
+ }),
382
+ ];
383
+ const newActivities = [
384
+ createMockAggregatedActivity({
385
+ group: 'group1',
386
+ activity_count: 10,
387
+ score: 100,
388
+ }),
389
+ createMockAggregatedActivity({
390
+ group: 'group3',
391
+ activity_count: 30,
392
+ score: 300,
393
+ }),
394
+ createMockAggregatedActivity({
395
+ group: 'group4',
396
+ activity_count: 4,
397
+ score: 40,
398
+ }),
399
+ ];
400
+
401
+ const result = addAggregatedActivitiesToState(
402
+ newActivities,
403
+ existingActivities,
404
+ 'replace',
405
+ );
406
+
407
+ expect(result.changed).toBe(true);
408
+ expect(result.aggregated_activities).toHaveLength(4);
409
+
410
+ // Check that group1 was replaced
411
+ const replacedGroup1 = result.aggregated_activities.find(
412
+ (a) => a.group === 'group1',
413
+ );
414
+ expect(replacedGroup1?.activity_count).toBe(10);
415
+ expect(replacedGroup1?.score).toBe(100);
416
+
417
+ // Check that group2 remains unchanged
418
+ const unchangedGroup2 = result.aggregated_activities.find(
419
+ (a) => a.group === 'group2',
420
+ );
421
+ expect(unchangedGroup2?.activity_count).toBe(2);
422
+ expect(unchangedGroup2?.score).toBe(20);
423
+
424
+ // Check that group3 was replaced
425
+ const replacedGroup3 = result.aggregated_activities.find(
426
+ (a) => a.group === 'group3',
427
+ );
428
+ expect(replacedGroup3?.activity_count).toBe(30);
429
+ expect(replacedGroup3?.score).toBe(300);
430
+
431
+ // Check that group4 was added
432
+ const newGroup4 = result.aggregated_activities.find(
433
+ (a) => a.group === 'group4',
434
+ );
435
+ expect(newGroup4?.activity_count).toBe(4);
436
+ expect(newGroup4?.score).toBe(40);
437
+ });
438
+ });
439
+
440
+ it(`should merge update activities in the group`, async () => {
441
+ const existingActivities = [
442
+ createMockAggregatedActivity({
443
+ group: 'group1',
444
+ activities: [
445
+ generateActivityResponse({
446
+ id: 'activity0',
447
+ own_reactions: [
448
+ generateFeedReactionResponse({
449
+ type: 'like',
450
+ }),
451
+ ],
452
+ }),
453
+ ],
454
+ }),
455
+ ];
456
+
457
+ const newActivities = [
458
+ createMockAggregatedActivity({
459
+ group: 'group1',
460
+ activities: [
461
+ generateActivityResponse({
462
+ // Activity is updated
463
+ id: 'activity0',
464
+ is_watched: true,
465
+ // WS events don't include own_ fields
466
+ }),
467
+ generateActivityResponse({
468
+ id: 'activity1',
469
+ }),
470
+ ],
471
+ }),
472
+ createMockAggregatedActivity({
473
+ group: 'group2',
474
+ activity_count: 30,
475
+ score: 300,
476
+ activities: [
477
+ generateActivityResponse({
478
+ id: 'activity2',
479
+ }),
480
+ ],
481
+ }),
482
+ ];
483
+
484
+ const result = addAggregatedActivitiesToState(
485
+ newActivities,
486
+ existingActivities,
487
+ 'replace',
488
+ );
489
+
490
+ expect(result.changed).toBe(true);
491
+ expect(result.aggregated_activities).toHaveLength(2);
492
+
493
+ // Check that activity1 was updated
494
+ const updatedActivity = result.aggregated_activities[0].activities.find(
495
+ (a) => a.id === 'activity0',
496
+ );
497
+ expect(updatedActivity?.is_watched).toBe(true);
498
+ expect(updatedActivity?.own_reactions.length).toBe(1);
499
+
500
+ const newActivity1 = result.aggregated_activities[0].activities.find(
501
+ (a) => a.id === 'activity1',
502
+ );
503
+ expect(newActivity1).toBeDefined();
504
+
505
+ const newActivity2 = result.aggregated_activities[1].activities.find(
506
+ (a) => a.id === 'activity2',
507
+ );
508
+ expect(newActivity2).toBeDefined();
509
+ });
510
+ });
@@ -0,0 +1,72 @@
1
+ import type { AggregatedActivityResponse } from '../../gen/models';
2
+ import type { UpdateStateResult } from '../../types-internal';
3
+ import { replaceUniqueArrayMerge, uniqueArrayMerge } from '../../utils';
4
+ import { updateActivity } from './activity-updater';
5
+
6
+ export const addAggregatedActivitiesToState = (
7
+ newAggregatedActivities: AggregatedActivityResponse[],
8
+ aggregatedActivities: AggregatedActivityResponse[] | undefined,
9
+ position: 'start' | 'end' | 'replace',
10
+ ) => {
11
+ let result: UpdateStateResult<{
12
+ aggregated_activities: AggregatedActivityResponse[];
13
+ }>;
14
+ if (newAggregatedActivities.length === 0) {
15
+ result = {
16
+ changed: false,
17
+ aggregated_activities: aggregatedActivities ?? [],
18
+ };
19
+ } else {
20
+ result = {
21
+ changed: true,
22
+ aggregated_activities: [],
23
+ };
24
+
25
+ // Merge update activities in the group
26
+ newAggregatedActivities.forEach((newAggregatedActivity) => {
27
+ const existingAggregatedActivity = aggregatedActivities?.find(
28
+ (a) => a.group === newAggregatedActivity.group,
29
+ );
30
+ if (existingAggregatedActivity) {
31
+ for (let i = 0; i < newAggregatedActivity.activities.length; i++) {
32
+ const activity = newAggregatedActivity.activities[i];
33
+ const existingActivity = existingAggregatedActivity.activities.find(
34
+ (a) => a.id === activity.id,
35
+ );
36
+ if (existingActivity) {
37
+ newAggregatedActivity.activities[i] = updateActivity({
38
+ currentActivity: existingActivity,
39
+ newActivtiy: activity,
40
+ });
41
+ }
42
+ }
43
+ }
44
+ });
45
+
46
+ switch (position) {
47
+ case 'start':
48
+ result.aggregated_activities = uniqueArrayMerge(
49
+ newAggregatedActivities,
50
+ aggregatedActivities ?? [],
51
+ (a) => a.group,
52
+ );
53
+ break;
54
+ case 'end':
55
+ result.aggregated_activities = uniqueArrayMerge(
56
+ aggregatedActivities ?? [],
57
+ newAggregatedActivities,
58
+ (a) => a.group,
59
+ );
60
+ break;
61
+ case 'replace':
62
+ result.aggregated_activities = replaceUniqueArrayMerge(
63
+ aggregatedActivities ?? [],
64
+ newAggregatedActivities,
65
+ (a) => a.group,
66
+ );
67
+ break;
68
+ }
69
+ }
70
+
71
+ return result;
72
+ };
@@ -1,5 +1,5 @@
1
1
  import type { Feed } from '../../feed';
2
- import type { EventPayload} from '../../../types-internal';
2
+ import type { EventPayload } from '../../../types-internal';
3
3
  import { type PartializeAllBut } from '../../../types-internal';
4
4
  import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
5
5
 
@@ -53,7 +53,6 @@ export function handleCommentReactionAdded(
53
53
  newComments[commentIndex] = {
54
54
  ...newComments[commentIndex],
55
55
  reaction_count: comment.reaction_count ?? 0,
56
- // TODO: FIXME this should be handled by the backend
57
56
  latest_reactions: comment.latest_reactions ?? [],
58
57
  reaction_groups: comment.reaction_groups ?? {},
59
58
  own_reactions: ownReactions,