@stream-io/feeds-client 0.2.0 → 0.2.2
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/@react-bindings/hooks/feed-state-hooks/index.ts +4 -0
- package/CHANGELOG.md +16 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/index.d.ts +4 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useAggregatedActivities.d.ts +11 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts +6 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useIsAggregatedActivitySeen.d.ts +6 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useNotificationStatus.d.ts +13 -0
- package/dist/@react-bindings/wrappers/StreamFeed.d.ts +1 -1
- package/dist/index-react-bindings.browser.cjs +505 -222
- package/dist/index-react-bindings.browser.cjs.map +1 -1
- package/dist/index-react-bindings.browser.js +502 -223
- package/dist/index-react-bindings.browser.js.map +1 -1
- package/dist/index-react-bindings.node.cjs +505 -222
- package/dist/index-react-bindings.node.cjs.map +1 -1
- package/dist/index-react-bindings.node.js +502 -223
- package/dist/index-react-bindings.node.js.map +1 -1
- package/dist/index.browser.cjs +440 -205
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +440 -206
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +440 -205
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +440 -206
- package/dist/index.node.js.map +1 -1
- package/dist/src/feed/event-handlers/activity/handle-activity-deleted.d.ts +12 -3
- package/dist/src/feed/event-handlers/activity/handle-activity-marked.d.ts +11 -0
- package/dist/src/feed/event-handlers/activity/handle-activity-pinned.d.ts +3 -0
- package/dist/src/feed/event-handlers/activity/handle-activity-reaction-added.d.ts +10 -6
- package/dist/src/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts +10 -6
- package/dist/src/feed/event-handlers/activity/handle-activity-unpinned.d.ts +3 -0
- package/dist/src/feed/event-handlers/activity/handle-activity-updated.d.ts +7 -3
- package/dist/src/feed/event-handlers/activity/index.d.ts +1 -0
- package/dist/src/feed/event-handlers/bookmark/handle-bookmark-added.d.ts +10 -6
- package/dist/src/feed/event-handlers/bookmark/handle-bookmark-deleted.d.ts +10 -6
- package/dist/src/feed/event-handlers/bookmark/handle-bookmark-updated.d.ts +10 -6
- package/dist/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +8 -1
- package/dist/src/feed/feed.d.ts +2 -2
- package/dist/src/gen/models/index.d.ts +36 -1
- package/dist/src/test-utils/response-generators.d.ts +66 -1
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/update-entity-in-array.d.ts +27 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/feed/event-handlers/activity/activity-marked-utils.test.ts +208 -0
- package/src/feed/event-handlers/activity/activity-reaction-utils.test.ts +108 -96
- package/src/feed/event-handlers/activity/activity-utils.test.ts +84 -122
- package/src/feed/event-handlers/activity/handle-activity-deleted.ts +43 -10
- package/src/feed/event-handlers/activity/handle-activity-marked.ts +68 -0
- package/src/feed/event-handlers/activity/handle-activity-pinned.test.ts +60 -0
- package/src/feed/event-handlers/activity/handle-activity-pinned.ts +30 -0
- package/src/feed/event-handlers/activity/handle-activity-reaction-added.test.ts +157 -0
- package/src/feed/event-handlers/activity/handle-activity-reaction-added.ts +82 -40
- package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +200 -0
- package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.ts +89 -51
- package/src/feed/event-handlers/activity/handle-activity-unpinned.test.ts +95 -0
- package/src/feed/event-handlers/activity/handle-activity-unpinned.ts +30 -0
- package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +115 -0
- package/src/feed/event-handlers/activity/handle-activity-updated.ts +73 -35
- package/src/feed/event-handlers/activity/index.ts +2 -1
- package/src/feed/event-handlers/bookmark/bookmark-utils.test.ts +121 -109
- package/src/feed/event-handlers/bookmark/handle-bookmark-added.test.ts +178 -0
- package/src/feed/event-handlers/bookmark/handle-bookmark-added.ts +82 -39
- package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.test.ts +188 -0
- package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.ts +86 -48
- package/src/feed/event-handlers/bookmark/handle-bookmark-updated.test.ts +196 -0
- package/src/feed/event-handlers/bookmark/handle-bookmark-updated.ts +83 -44
- package/src/feed/event-handlers/comment/handle-comment-added.test.ts +147 -0
- package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +133 -0
- package/src/feed/event-handlers/comment/handle-comment-deleted.ts +24 -10
- package/src/feed/event-handlers/comment/handle-comment-reaction.test.ts +315 -0
- package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +131 -0
- package/src/feed/event-handlers/follow/handle-follow-created.test.ts +7 -7
- package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +2 -2
- package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +1 -1
- package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.test.ts +120 -0
- package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +47 -3
- package/src/feed/feed.ts +4 -2
- package/src/gen/model-decoders/decoders.ts +14 -1
- package/src/gen/models/index.ts +73 -2
- package/src/gen/moderation/ModerationApi.ts +1 -0
- package/src/test-utils/response-generators.ts +383 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/update-entity-in-array.ts +51 -0
|
@@ -1,55 +1,19 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { ActivityResponse
|
|
2
|
+
import { ActivityResponse } from '../../../gen/models';
|
|
3
3
|
import {
|
|
4
4
|
addActivitiesToState,
|
|
5
5
|
updateActivityInState,
|
|
6
6
|
removeActivityFromState,
|
|
7
7
|
} from './';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type: 'test',
|
|
13
|
-
created_at: new Date(),
|
|
14
|
-
updated_at: new Date(),
|
|
15
|
-
visibility: 'public',
|
|
16
|
-
bookmark_count: 0,
|
|
17
|
-
comment_count: 0,
|
|
18
|
-
share_count: 0,
|
|
19
|
-
attachments: [],
|
|
20
|
-
comments: [],
|
|
21
|
-
feeds: [],
|
|
22
|
-
filter_tags: [],
|
|
23
|
-
interest_tags: [],
|
|
24
|
-
latest_reactions: [],
|
|
25
|
-
mentioned_users: [],
|
|
26
|
-
own_bookmarks: [],
|
|
27
|
-
own_reactions: [],
|
|
28
|
-
custom: {},
|
|
29
|
-
reaction_groups: {},
|
|
30
|
-
search_data: {},
|
|
31
|
-
text: text,
|
|
32
|
-
popularity: 0,
|
|
33
|
-
score: 0,
|
|
34
|
-
reaction_count: 0,
|
|
35
|
-
user: {
|
|
36
|
-
id: 'user1',
|
|
37
|
-
created_at: new Date(),
|
|
38
|
-
updated_at: new Date(),
|
|
39
|
-
banned: false,
|
|
40
|
-
language: 'en',
|
|
41
|
-
online: false,
|
|
42
|
-
role: 'user',
|
|
43
|
-
blocked_user_ids: [],
|
|
44
|
-
teams: [],
|
|
45
|
-
custom: {},
|
|
46
|
-
},
|
|
47
|
-
}) as ActivityResponse;
|
|
8
|
+
import {
|
|
9
|
+
generateActivityResponse,
|
|
10
|
+
generateFeedReactionResponse,
|
|
11
|
+
} from '../../../test-utils';
|
|
48
12
|
|
|
49
13
|
describe('activity-utils', () => {
|
|
50
14
|
describe('addActivitiesToState', () => {
|
|
51
|
-
const activity1 =
|
|
52
|
-
const activity2 =
|
|
15
|
+
const activity1 = generateActivityResponse({ id: 'activity1' });
|
|
16
|
+
const activity2 = generateActivityResponse({ id: 'activity2' });
|
|
53
17
|
|
|
54
18
|
it('should add activities to empty state', () => {
|
|
55
19
|
const result = addActivitiesToState([activity1], undefined, 'start');
|
|
@@ -101,7 +65,7 @@ describe('activity-utils', () => {
|
|
|
101
65
|
});
|
|
102
66
|
|
|
103
67
|
it('should handle multiple new activities correctly', () => {
|
|
104
|
-
const activity3 =
|
|
68
|
+
const activity3 = generateActivityResponse({ id: 'activity3' });
|
|
105
69
|
|
|
106
70
|
const existingActivities = [activity1];
|
|
107
71
|
const result = addActivitiesToState(
|
|
@@ -120,109 +84,107 @@ describe('activity-utils', () => {
|
|
|
120
84
|
|
|
121
85
|
describe('updateActivityInState', () => {
|
|
122
86
|
it('should update an activity in the state', () => {
|
|
123
|
-
const originalActivity =
|
|
124
|
-
|
|
87
|
+
const originalActivity = generateActivityResponse({
|
|
88
|
+
id: 'activity1',
|
|
89
|
+
text: 'original text',
|
|
90
|
+
});
|
|
91
|
+
const updatedActivity = { ...originalActivity, text: 'updated text' };
|
|
125
92
|
const originalActivities = [originalActivity];
|
|
126
93
|
|
|
127
|
-
const result = updateActivityInState(
|
|
94
|
+
const result = updateActivityInState(
|
|
95
|
+
{
|
|
96
|
+
activity: updatedActivity,
|
|
97
|
+
created_at: new Date(),
|
|
98
|
+
fid: '',
|
|
99
|
+
type: '',
|
|
100
|
+
custom: {},
|
|
101
|
+
},
|
|
102
|
+
originalActivities,
|
|
103
|
+
);
|
|
128
104
|
|
|
129
105
|
expect(result.changed).toBe(true);
|
|
130
|
-
expect(result.
|
|
131
|
-
expect(result.
|
|
132
|
-
expect(result.
|
|
106
|
+
expect(result.entities).toHaveLength(1);
|
|
107
|
+
expect(result.entities![0].id).toBe('activity1');
|
|
108
|
+
expect(result.entities![0].text).toBe('updated text');
|
|
133
109
|
|
|
134
110
|
// Make sure we create a new array
|
|
135
|
-
expect(originalActivities === result.
|
|
111
|
+
expect(originalActivities === result.entities).toBe(false);
|
|
136
112
|
});
|
|
137
113
|
|
|
138
|
-
it('should preserve reaction data when updating an activity', () => {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
type: 'like',
|
|
144
|
-
user: {
|
|
145
|
-
id: 'user1',
|
|
146
|
-
created_at: new Date(),
|
|
147
|
-
updated_at: new Date(),
|
|
148
|
-
banned: false,
|
|
149
|
-
language: 'en',
|
|
150
|
-
online: false,
|
|
151
|
-
role: 'user',
|
|
152
|
-
blocked_user_ids: [],
|
|
153
|
-
teams: [],
|
|
154
|
-
custom: {},
|
|
155
|
-
},
|
|
156
|
-
activity_id: 'activity1',
|
|
157
|
-
created_at: new Date(),
|
|
158
|
-
updated_at: new Date(),
|
|
114
|
+
it('should preserve reaction data (own_reaction) when updating an activity', () => {
|
|
115
|
+
const r = generateFeedReactionResponse({
|
|
116
|
+
activity_id: 'activity1',
|
|
117
|
+
user: {
|
|
118
|
+
id: 'user1',
|
|
159
119
|
},
|
|
160
|
-
|
|
161
|
-
originalActivity
|
|
162
|
-
|
|
120
|
+
});
|
|
121
|
+
const originalActivity = generateActivityResponse({
|
|
122
|
+
id: 'activity1',
|
|
123
|
+
text: 'original text',
|
|
124
|
+
own_reactions: [r],
|
|
125
|
+
latest_reactions: [r],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const updatedActivity = generateActivityResponse({
|
|
129
|
+
id: 'activity1',
|
|
130
|
+
text: 'updated text',
|
|
131
|
+
own_reactions: [],
|
|
132
|
+
latest_reactions: [
|
|
133
|
+
r,
|
|
134
|
+
generateFeedReactionResponse({
|
|
135
|
+
activity_id: 'activity1',
|
|
136
|
+
user: { id: 'user2' },
|
|
137
|
+
}),
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const result = updateActivityInState(
|
|
163
142
|
{
|
|
164
|
-
|
|
165
|
-
user: {
|
|
166
|
-
id: 'user1',
|
|
167
|
-
created_at: new Date(),
|
|
168
|
-
updated_at: new Date(),
|
|
169
|
-
banned: false,
|
|
170
|
-
language: 'en',
|
|
171
|
-
online: false,
|
|
172
|
-
role: 'user',
|
|
173
|
-
blocked_user_ids: [],
|
|
174
|
-
teams: [],
|
|
175
|
-
custom: {},
|
|
176
|
-
},
|
|
177
|
-
activity_id: 'activity1',
|
|
143
|
+
activity: updatedActivity,
|
|
178
144
|
created_at: new Date(),
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
originalActivity.reaction_groups = {
|
|
183
|
-
like: {
|
|
184
|
-
sum_scores: 0,
|
|
185
|
-
count: 1,
|
|
186
|
-
first_reaction_at: new Date(),
|
|
187
|
-
last_reaction_at: new Date(),
|
|
145
|
+
fid: '',
|
|
146
|
+
type: '',
|
|
147
|
+
custom: {},
|
|
188
148
|
},
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const updatedActivity = createMockActivity('activity1', 'updated text');
|
|
192
|
-
// Reactions are not included in the updated activity from server
|
|
193
|
-
|
|
194
|
-
const result = updateActivityInState(updatedActivity, [originalActivity]);
|
|
149
|
+
[originalActivity],
|
|
150
|
+
);
|
|
195
151
|
|
|
196
152
|
expect(result.changed).toBe(true);
|
|
197
|
-
expect(result.
|
|
153
|
+
expect(result.entities![0].text).toBe('updated text');
|
|
198
154
|
// Check that reactions were preserved
|
|
199
|
-
expect(result.
|
|
155
|
+
expect(result.entities![0].own_reactions).toEqual(
|
|
200
156
|
originalActivity.own_reactions,
|
|
201
157
|
);
|
|
202
|
-
expect(result.activities[0].latest_reactions).toEqual(
|
|
203
|
-
originalActivity.latest_reactions,
|
|
204
|
-
);
|
|
205
|
-
expect(result.activities[0].reaction_groups).toEqual(
|
|
206
|
-
originalActivity.reaction_groups,
|
|
207
|
-
);
|
|
208
158
|
});
|
|
209
159
|
|
|
210
160
|
it('should return unchanged state if activity not found', () => {
|
|
211
|
-
const existingActivity =
|
|
212
|
-
const updatedActivity =
|
|
161
|
+
const existingActivity = generateActivityResponse({ id: 'activity1' });
|
|
162
|
+
const updatedActivity = generateActivityResponse({
|
|
163
|
+
id: 'activity2',
|
|
164
|
+
text: 'some text',
|
|
165
|
+
});
|
|
213
166
|
|
|
214
|
-
const result = updateActivityInState(
|
|
167
|
+
const result = updateActivityInState(
|
|
168
|
+
{
|
|
169
|
+
activity: updatedActivity,
|
|
170
|
+
created_at: new Date(),
|
|
171
|
+
fid: '',
|
|
172
|
+
type: '',
|
|
173
|
+
custom: {},
|
|
174
|
+
},
|
|
175
|
+
[existingActivity],
|
|
176
|
+
);
|
|
215
177
|
|
|
216
178
|
expect(result.changed).toBe(false);
|
|
217
|
-
expect(result.
|
|
218
|
-
expect(result.
|
|
179
|
+
expect(result.entities).toHaveLength(1);
|
|
180
|
+
expect(result.entities![0].id).toBe('activity1');
|
|
219
181
|
});
|
|
220
182
|
});
|
|
221
183
|
|
|
222
184
|
describe('removeActivityFromState', () => {
|
|
223
185
|
it('should remove an activity from the state', () => {
|
|
224
|
-
const activity1 =
|
|
225
|
-
const activity2 =
|
|
186
|
+
const activity1 = generateActivityResponse({ id: 'activity1' });
|
|
187
|
+
const activity2 = generateActivityResponse({ id: 'activity2' });
|
|
226
188
|
const activities = [activity1, activity2];
|
|
227
189
|
|
|
228
190
|
const result = removeActivityFromState(activity1, activities);
|
|
@@ -235,8 +197,8 @@ describe('activity-utils', () => {
|
|
|
235
197
|
});
|
|
236
198
|
|
|
237
199
|
it('should return unchanged state if activity not found', () => {
|
|
238
|
-
const activity1 =
|
|
239
|
-
const activity2 =
|
|
200
|
+
const activity1 = generateActivityResponse({ id: 'activity1' });
|
|
201
|
+
const activity2 = generateActivityResponse({ id: 'activity2' });
|
|
240
202
|
const activities = [activity1];
|
|
241
203
|
|
|
242
204
|
const result = removeActivityFromState(activity2, activities);
|
|
@@ -247,7 +209,7 @@ describe('activity-utils', () => {
|
|
|
247
209
|
});
|
|
248
210
|
|
|
249
211
|
it('should handle empty activities array', () => {
|
|
250
|
-
const activity =
|
|
212
|
+
const activity = generateActivityResponse({ id: 'activity1' });
|
|
251
213
|
const activities: ActivityResponse[] = [];
|
|
252
214
|
|
|
253
215
|
const result = removeActivityFromState(activity, activities);
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { Feed } from '../../../feed';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ActivityPinResponse,
|
|
4
|
+
ActivityResponse,
|
|
5
|
+
} from '../../../gen/models';
|
|
3
6
|
import type { EventPayload } from '../../../types-internal';
|
|
4
7
|
|
|
5
8
|
export const removeActivityFromState = (
|
|
6
9
|
activityResponse: ActivityResponse,
|
|
7
|
-
activities: ActivityResponse[],
|
|
10
|
+
activities: ActivityResponse[] | undefined,
|
|
8
11
|
) => {
|
|
9
|
-
const index =
|
|
12
|
+
const index =
|
|
13
|
+
activities?.findIndex((activity) => activity.id === activityResponse.id) ??
|
|
14
|
+
-1;
|
|
15
|
+
|
|
10
16
|
if (index !== -1) {
|
|
11
|
-
const newActivities = [...activities];
|
|
17
|
+
const newActivities = [...activities!];
|
|
12
18
|
newActivities.splice(index, 1);
|
|
13
19
|
return { changed: true, activities: newActivities };
|
|
14
20
|
} else {
|
|
@@ -16,15 +22,42 @@ export const removeActivityFromState = (
|
|
|
16
22
|
}
|
|
17
23
|
};
|
|
18
24
|
|
|
25
|
+
export const removePinnedActivityFromState = (
|
|
26
|
+
activityResponse: ActivityResponse,
|
|
27
|
+
pinnedActivities: ActivityPinResponse[] | undefined,
|
|
28
|
+
) => {
|
|
29
|
+
const index =
|
|
30
|
+
pinnedActivities?.findIndex(
|
|
31
|
+
(pinnedActivity) => pinnedActivity.activity.id === activityResponse.id,
|
|
32
|
+
) ?? -1;
|
|
33
|
+
|
|
34
|
+
if (index !== -1) {
|
|
35
|
+
const newActivities = [...pinnedActivities!];
|
|
36
|
+
newActivities.splice(index, 1);
|
|
37
|
+
return { changed: true, activities: newActivities };
|
|
38
|
+
} else {
|
|
39
|
+
return { changed: false, pinned_activities: pinnedActivities };
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
19
43
|
export function handleActivityDeleted(
|
|
20
44
|
this: Feed,
|
|
21
45
|
event: EventPayload<'feeds.activity.deleted'>,
|
|
22
46
|
) {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
const {
|
|
48
|
+
activities: currentActivities,
|
|
49
|
+
pinned_activities: currentPinnedActivities,
|
|
50
|
+
} = this.currentState;
|
|
51
|
+
|
|
52
|
+
const [result1, result2] = [
|
|
53
|
+
removeActivityFromState(event.activity, currentActivities),
|
|
54
|
+
removePinnedActivityFromState(event.activity, currentPinnedActivities),
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
if (result1.changed || result2.changed) {
|
|
58
|
+
this.state.partialNext({
|
|
59
|
+
activities: result1.activities,
|
|
60
|
+
pinned_activities: result2.pinned_activities,
|
|
61
|
+
});
|
|
29
62
|
}
|
|
30
63
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActivityMarkEvent,
|
|
3
|
+
NotificationStatusResponse,
|
|
4
|
+
} from '../../../gen/models';
|
|
5
|
+
import { EventPayload, UpdateStateResult } from '../../../types-internal';
|
|
6
|
+
import { Feed } from '../../feed';
|
|
7
|
+
|
|
8
|
+
export const updateNotificationStatusFromActivityMarked = (
|
|
9
|
+
event: ActivityMarkEvent,
|
|
10
|
+
currentNotificationStatus: NotificationStatusResponse | undefined,
|
|
11
|
+
aggregatedActivities: Array<{ group: string }> = [],
|
|
12
|
+
): UpdateStateResult<{
|
|
13
|
+
data?: { notification_status: NotificationStatusResponse };
|
|
14
|
+
}> => {
|
|
15
|
+
if (!currentNotificationStatus) {
|
|
16
|
+
return {
|
|
17
|
+
changed: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const newState = {
|
|
22
|
+
...currentNotificationStatus,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (event.mark_all_read) {
|
|
26
|
+
const allGroupIds = aggregatedActivities.map((activity) => activity.group);
|
|
27
|
+
newState.read_activities = [
|
|
28
|
+
...new Set([
|
|
29
|
+
...(currentNotificationStatus.read_activities ?? []),
|
|
30
|
+
...allGroupIds,
|
|
31
|
+
]),
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (event.mark_read && event.mark_read.length > 0) {
|
|
36
|
+
newState.read_activities = [
|
|
37
|
+
...new Set([
|
|
38
|
+
...(currentNotificationStatus?.read_activities ?? []),
|
|
39
|
+
...event.mark_read,
|
|
40
|
+
]),
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (event.mark_all_seen) {
|
|
45
|
+
newState.last_seen_at = new Date();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
changed: true,
|
|
50
|
+
data: { notification_status: newState },
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export function handleActivityMarked(
|
|
55
|
+
this: Feed,
|
|
56
|
+
event: EventPayload<'feeds.activity.marked'>,
|
|
57
|
+
) {
|
|
58
|
+
const result = updateNotificationStatusFromActivityMarked(
|
|
59
|
+
event,
|
|
60
|
+
this.currentState.notification_status,
|
|
61
|
+
this.currentState.aggregated_activities,
|
|
62
|
+
);
|
|
63
|
+
if (result.changed) {
|
|
64
|
+
this.state.partialNext({
|
|
65
|
+
notification_status: result.data?.notification_status,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { Feed } from '../../../feed';
|
|
4
|
+
import { FeedsClient } from '../../../feeds-client';
|
|
5
|
+
import { handleActivityPinned } from './handle-activity-pinned';
|
|
6
|
+
import {
|
|
7
|
+
generateActivityPinnedEvent,
|
|
8
|
+
generateActivityPinResponse,
|
|
9
|
+
generateFeedResponse,
|
|
10
|
+
} from '../../../test-utils/response-generators';
|
|
11
|
+
import { ActivityPinResponse } from '../../../gen/models';
|
|
12
|
+
|
|
13
|
+
describe(handleActivityPinned.name, () => {
|
|
14
|
+
let feed: Feed;
|
|
15
|
+
let client: FeedsClient;
|
|
16
|
+
let pinnedActivity: ActivityPinResponse;
|
|
17
|
+
let otherPinnedActivity: ActivityPinResponse;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
client = new FeedsClient('mock-api-key');
|
|
21
|
+
const feedResponse = generateFeedResponse({ id: 'main', group_id: 'user' });
|
|
22
|
+
feed = new Feed(
|
|
23
|
+
client,
|
|
24
|
+
feedResponse.group_id,
|
|
25
|
+
feedResponse.id,
|
|
26
|
+
feedResponse,
|
|
27
|
+
);
|
|
28
|
+
pinnedActivity = generateActivityPinResponse();
|
|
29
|
+
otherPinnedActivity = generateActivityPinResponse();
|
|
30
|
+
feed.state.next((currentState) => ({
|
|
31
|
+
...currentState,
|
|
32
|
+
pinned_activities: [otherPinnedActivity],
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('adds a new activity to pinned_activities', () => {
|
|
37
|
+
const event = generateActivityPinnedEvent({
|
|
38
|
+
pinned_activity: pinnedActivity,
|
|
39
|
+
});
|
|
40
|
+
handleActivityPinned.call(feed, event);
|
|
41
|
+
const { pinned_activities } = feed.currentState;
|
|
42
|
+
expect(pinned_activities).toHaveLength(2);
|
|
43
|
+
expect(pinned_activities![0].activity.id).toBe(pinnedActivity.activity.id);
|
|
44
|
+
expect(pinned_activities![1]).toBe(otherPinnedActivity);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('creates pinned_activities if it was undefined', () => {
|
|
48
|
+
feed.state.next((currentState) => ({
|
|
49
|
+
...currentState,
|
|
50
|
+
pinned_activities: undefined,
|
|
51
|
+
}));
|
|
52
|
+
const event = generateActivityPinnedEvent({
|
|
53
|
+
pinned_activity: pinnedActivity,
|
|
54
|
+
});
|
|
55
|
+
handleActivityPinned.call(feed, event);
|
|
56
|
+
const { pinned_activities } = feed.currentState;
|
|
57
|
+
expect(pinned_activities).toHaveLength(1);
|
|
58
|
+
expect(pinned_activities![0].activity.id).toBe(pinnedActivity.activity.id);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ActivityPinResponse } from '../../../gen/models';
|
|
2
|
+
import { EventPayload } from '../../../types-internal';
|
|
3
|
+
import { Feed } from '../../feed';
|
|
4
|
+
|
|
5
|
+
export function handleActivityPinned(
|
|
6
|
+
this: Feed,
|
|
7
|
+
event: EventPayload<'feeds.activity.pinned'>,
|
|
8
|
+
) {
|
|
9
|
+
this.state.next((currentState) => {
|
|
10
|
+
const newState = {
|
|
11
|
+
...currentState,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// FIXME: type mismatch PinActivityResponse vs ActivityPinResponse (almost identical but not quite)
|
|
15
|
+
|
|
16
|
+
// re-map the event value to match the ActivityPinResponse type
|
|
17
|
+
const pinnedActivity: ActivityPinResponse = {
|
|
18
|
+
...event.pinned_activity,
|
|
19
|
+
user: event.user!,
|
|
20
|
+
feed: event.fid,
|
|
21
|
+
updated_at: new Date(),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
newState.pinned_activities = currentState.pinned_activities
|
|
25
|
+
? [pinnedActivity, ...currentState.pinned_activities]
|
|
26
|
+
: [pinnedActivity];
|
|
27
|
+
|
|
28
|
+
return newState;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { Feed } from '../../../feed';
|
|
3
|
+
import { FeedsClient } from '../../../feeds-client';
|
|
4
|
+
import { handleActivityReactionAdded } from './handle-activity-reaction-added';
|
|
5
|
+
import {
|
|
6
|
+
generateActivityPinResponse,
|
|
7
|
+
generateActivityResponse,
|
|
8
|
+
generateFeedResponse,
|
|
9
|
+
generateOwnUser,
|
|
10
|
+
generateActivityReactionAddedEvent,
|
|
11
|
+
getHumanId,
|
|
12
|
+
} from '../../../test-utils/response-generators';
|
|
13
|
+
|
|
14
|
+
describe(handleActivityReactionAdded.name, () => {
|
|
15
|
+
let feed: Feed;
|
|
16
|
+
let client: FeedsClient;
|
|
17
|
+
let currentUserId: string;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
client = new FeedsClient('mock-api-key');
|
|
21
|
+
currentUserId = getHumanId();
|
|
22
|
+
client.state.partialNext({
|
|
23
|
+
connected_user: generateOwnUser({ id: currentUserId }),
|
|
24
|
+
});
|
|
25
|
+
const feedResponse = generateFeedResponse({
|
|
26
|
+
id: 'main',
|
|
27
|
+
group_id: 'user',
|
|
28
|
+
created_by: { id: currentUserId },
|
|
29
|
+
});
|
|
30
|
+
feed = new Feed(
|
|
31
|
+
client,
|
|
32
|
+
feedResponse.group_id,
|
|
33
|
+
feedResponse.id,
|
|
34
|
+
feedResponse,
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('adds a reaction to the correct activity for current user & updates activities with event.activity', () => {
|
|
39
|
+
const event = generateActivityReactionAddedEvent({
|
|
40
|
+
reaction: {
|
|
41
|
+
user: { id: currentUserId },
|
|
42
|
+
},
|
|
43
|
+
activity: {
|
|
44
|
+
reaction_count: 1,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const activity = generateActivityResponse({
|
|
48
|
+
id: event.activity.id,
|
|
49
|
+
reaction_count: 0,
|
|
50
|
+
});
|
|
51
|
+
const activityPin = generateActivityPinResponse({
|
|
52
|
+
activity: { ...activity },
|
|
53
|
+
});
|
|
54
|
+
feed.state.partialNext({
|
|
55
|
+
activities: [activity],
|
|
56
|
+
pinned_activities: [activityPin],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const stateBefore = feed.currentState;
|
|
60
|
+
|
|
61
|
+
expect(stateBefore.activities![0].reaction_count).toBe(0);
|
|
62
|
+
expect(stateBefore.pinned_activities![0].activity.reaction_count).toBe(
|
|
63
|
+
0,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
handleActivityReactionAdded.call(feed, event);
|
|
67
|
+
|
|
68
|
+
const stateAfter = feed.currentState;
|
|
69
|
+
|
|
70
|
+
expect(stateAfter.activities![0].own_reactions).toContain(event.reaction);
|
|
71
|
+
expect(stateAfter.pinned_activities![0].activity.own_reactions).toContain(
|
|
72
|
+
event.reaction,
|
|
73
|
+
);
|
|
74
|
+
expect(stateAfter.activities![0].own_bookmarks).toBe(
|
|
75
|
+
stateBefore.activities![0].own_bookmarks,
|
|
76
|
+
);
|
|
77
|
+
expect(stateAfter.pinned_activities![0].activity.own_bookmarks).toBe(
|
|
78
|
+
stateBefore.pinned_activities![0].activity.own_bookmarks,
|
|
79
|
+
);
|
|
80
|
+
expect(stateAfter.activities![0].reaction_count).toBe(1);
|
|
81
|
+
expect(stateAfter.pinned_activities![0].activity.reaction_count).toBe(1);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('does not add to own_reactions if reaction is from another user but still updates activity', () => {
|
|
85
|
+
const event = generateActivityReactionAddedEvent({
|
|
86
|
+
reaction: { user: { id: 'other-user-id' } },
|
|
87
|
+
activity: {
|
|
88
|
+
reaction_count: 1,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
const activity = generateActivityResponse({
|
|
92
|
+
id: event.activity.id,
|
|
93
|
+
reaction_count: 0,
|
|
94
|
+
});
|
|
95
|
+
const activityPin = generateActivityPinResponse({
|
|
96
|
+
activity: { ...activity },
|
|
97
|
+
});
|
|
98
|
+
feed.state.partialNext({
|
|
99
|
+
activities: [activity],
|
|
100
|
+
pinned_activities: [activityPin],
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const stateBefore = feed.currentState;
|
|
104
|
+
|
|
105
|
+
expect(stateBefore.activities![0].reaction_count).toBe(0);
|
|
106
|
+
expect(stateBefore.pinned_activities![0].activity.reaction_count).toBe(
|
|
107
|
+
0,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
handleActivityReactionAdded.call(feed, event);
|
|
111
|
+
|
|
112
|
+
const stateAfter = feed.currentState;
|
|
113
|
+
|
|
114
|
+
expect(stateAfter.activities![0].own_reactions).toHaveLength(0);
|
|
115
|
+
expect(
|
|
116
|
+
stateAfter.pinned_activities![0].activity.own_reactions,
|
|
117
|
+
).toHaveLength(0);
|
|
118
|
+
expect(stateAfter.activities![0].reaction_count).toBe(1);
|
|
119
|
+
expect(stateAfter.pinned_activities![0].activity.reaction_count).toBe(1);
|
|
120
|
+
expect(stateAfter.activities![0].own_bookmarks).toBe(
|
|
121
|
+
stateBefore.activities![0].own_bookmarks,
|
|
122
|
+
);
|
|
123
|
+
expect(stateAfter.pinned_activities![0].activity.own_bookmarks).toBe(
|
|
124
|
+
stateBefore.pinned_activities![0].activity.own_bookmarks,
|
|
125
|
+
);
|
|
126
|
+
expect(stateAfter.activities![0].own_reactions).toBe(
|
|
127
|
+
stateBefore.activities![0].own_reactions,
|
|
128
|
+
);
|
|
129
|
+
expect(stateAfter.pinned_activities![0].activity.own_reactions).toBe(
|
|
130
|
+
stateBefore.pinned_activities![0].activity.own_reactions,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('does nothing if activity is not found', () => {
|
|
135
|
+
const event = generateActivityReactionAddedEvent({
|
|
136
|
+
reaction: { user: { id: currentUserId } },
|
|
137
|
+
});
|
|
138
|
+
const activity = generateActivityResponse({
|
|
139
|
+
id: 'unrelated',
|
|
140
|
+
});
|
|
141
|
+
const activityPin = generateActivityPinResponse({
|
|
142
|
+
activity: { ...activity },
|
|
143
|
+
});
|
|
144
|
+
feed.state.partialNext({
|
|
145
|
+
activities: [activity],
|
|
146
|
+
pinned_activities: [activityPin],
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const stateBefore = feed.currentState;
|
|
150
|
+
|
|
151
|
+
handleActivityReactionAdded.call(feed, event);
|
|
152
|
+
|
|
153
|
+
const stateAfter = feed.currentState;
|
|
154
|
+
|
|
155
|
+
expect(stateAfter).toBe(stateBefore);
|
|
156
|
+
});
|
|
157
|
+
});
|