@stream-io/feeds-client 0.2.11 → 0.2.13
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 +17 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react-bindings.js +56 -46
- package/dist/cjs/react-bindings.js.map +1 -1
- package/dist/es/index.mjs +2 -2
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/react-bindings.mjs +56 -46
- package/dist/es/react-bindings.mjs.map +1 -1
- package/dist/{index-CFv0uza2.js → index-RzB4c4g6.js} +98 -28
- package/dist/index-RzB4c4g6.js.map +1 -0
- package/dist/{index-DP0C8psw.mjs → index-gvcJhGPH.mjs} +98 -28
- package/dist/index-gvcJhGPH.mjs.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useAggregatedActivities.d.ts +9 -4
- package/dist/types/bindings/react/hooks/feed-state-hooks/useAggregatedActivities.d.ts.map +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts.map +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts +15 -16
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts.map +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnFollows.d.ts +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnFollows.d.ts.map +1 -1
- package/dist/types/bindings/react/hooks/util/useReactionActions.d.ts +1 -1
- package/dist/types/bindings/react/hooks/util/useReactionActions.d.ts.map +1 -1
- package/dist/types/bindings/react/wrappers/StreamFeed.d.ts +1 -1
- package/dist/types/bindings/react/wrappers/StreamFeed.d.ts.map +1 -1
- package/dist/types/bindings/react/wrappers/StreamFeeds.d.ts +1 -1
- package/dist/types/bindings/react/wrappers/StreamFeeds.d.ts.map +1 -1
- package/dist/types/bindings/react/wrappers/StreamSearch.d.ts +1 -1
- package/dist/types/bindings/react/wrappers/StreamSearch.d.ts.map +1 -1
- package/dist/types/bindings/react/wrappers/StreamSearchResults.d.ts +1 -1
- package/dist/types/bindings/react/wrappers/StreamSearchResults.d.ts.map +1 -1
- package/dist/types/common/ApiClient.d.ts +3 -3
- package/dist/types/common/ApiClient.d.ts.map +1 -1
- package/dist/types/common/EventDispatcher.d.ts +1 -1
- package/dist/types/common/EventDispatcher.d.ts.map +1 -1
- package/dist/types/common/rate-limit.d.ts +1 -1
- package/dist/types/common/rate-limit.d.ts.map +1 -1
- package/dist/types/common/real-time/StableWSConnection.d.ts +3 -3
- package/dist/types/common/real-time/StableWSConnection.d.ts.map +1 -1
- package/dist/types/common/real-time/event-models.d.ts +1 -1
- package/dist/types/common/real-time/event-models.d.ts.map +1 -1
- package/dist/types/common/search/ActivitySearchSource.d.ts +2 -2
- package/dist/types/common/search/ActivitySearchSource.d.ts.map +1 -1
- package/dist/types/common/search/FeedSearchSource.d.ts +2 -2
- package/dist/types/common/search/FeedSearchSource.d.ts.map +1 -1
- package/dist/types/common/search/UserSearchSource.d.ts +2 -2
- package/dist/types/common/search/UserSearchSource.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts +3 -3
- package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-marked.d.ts +3 -3
- package/dist/types/feed/event-handlers/activity/handle-activity-marked.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-pinned.d.ts +2 -2
- package/dist/types/feed/event-handlers/activity/handle-activity-pinned.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts +1 -1
- 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-removed-from-feed.d.ts +2 -2
- package/dist/types/feed/event-handlers/activity/handle-activity-removed-from-feed.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-unpinned.d.ts +2 -2
- package/dist/types/feed/event-handlers/activity/handle-activity-unpinned.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts +3 -3
- package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-deleted.d.ts +3 -2
- 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 +3 -2
- 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-deleted.d.ts +3 -2
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-deleted.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-updated.d.ts +3 -2
- package/dist/types/feed/event-handlers/comment/handle-comment-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/utils/update-comment-count.d.ts +2 -2
- package/dist/types/feed/event-handlers/comment/utils/update-comment-count.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/feed/handle-feed-updated.d.ts +2 -2
- package/dist/types/feed/event-handlers/feed/handle-feed-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/feed-member/handle-feed-member-added.d.ts +2 -2
- package/dist/types/feed/event-handlers/feed-member/handle-feed-member-added.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/feed-member/handle-feed-member-removed.d.ts +2 -2
- package/dist/types/feed/event-handlers/feed-member/handle-feed-member-removed.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/feed-member/handle-feed-member-updated.d.ts +2 -2
- package/dist/types/feed/event-handlers/feed-member/handle-feed-member-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/follow/handle-follow-updated.d.ts +1 -1
- package/dist/types/feed/event-handlers/follow/handle-follow-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +9 -2
- package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/watch/handle-watch-started.d.ts +1 -1
- package/dist/types/feed/event-handlers/watch/handle-watch-started.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/watch/handle-watch-stopped.d.ts +1 -1
- package/dist/types/feed/event-handlers/watch/handle-watch-stopped.d.ts.map +1 -1
- package/dist/types/feed/feed.d.ts +3 -3
- package/dist/types/feed/feed.d.ts.map +1 -1
- package/dist/types/feeds-client/feeds-client.d.ts +3 -3
- package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
- package/dist/types/gen/feeds/FeedApi.d.ts +2 -2
- package/dist/types/gen/feeds/FeedApi.d.ts.map +1 -1
- package/dist/types/gen/feeds/FeedsApi.d.ts +2 -2
- package/dist/types/gen/feeds/FeedsApi.d.ts.map +1 -1
- package/dist/types/gen/model-decoders/event-decoder-mapping.d.ts +1 -1
- package/dist/types/gen/model-decoders/event-decoder-mapping.d.ts.map +1 -1
- package/dist/types/gen/models/index.d.ts +24 -26
- package/dist/types/gen/models/index.d.ts.map +1 -1
- package/dist/types/gen/moderation/ModerationApi.d.ts +2 -2
- package/dist/types/gen/moderation/ModerationApi.d.ts.map +1 -1
- package/dist/types/types-internal.d.ts +1 -1
- package/dist/types/types-internal.d.ts.map +1 -1
- package/dist/types/types.d.ts +4 -4
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/event-triggered-by-connected-user.d.ts +2 -2
- package/dist/types/utils/event-triggered-by-connected-user.d.ts.map +1 -1
- package/dist/types/utils/state-update-queue.d.ts +2 -2
- package/dist/types/utils/state-update-queue.d.ts.map +1 -1
- package/dist/types/utils/type-assertions.d.ts +3 -4
- package/dist/types/utils/type-assertions.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/bindings/react/hooks/feed-state-hooks/useAggregatedActivities.ts +28 -4
- package/src/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.ts +4 -5
- package/src/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.ts +52 -38
- package/src/bindings/react/hooks/feed-state-hooks/useOwnFollows.ts +1 -1
- package/src/bindings/react/hooks/util/useReactionActions.ts +2 -1
- package/src/bindings/react/wrappers/StreamFeed.tsx +1 -1
- package/src/bindings/react/wrappers/StreamFeeds.tsx +1 -1
- package/src/bindings/react/wrappers/StreamSearch.tsx +1 -1
- package/src/bindings/react/wrappers/StreamSearchResults.tsx +1 -1
- package/src/common/ApiClient.ts +6 -4
- package/src/common/EventDispatcher.ts +1 -1
- package/src/common/rate-limit.ts +1 -1
- package/src/common/real-time/StableWSConnection.ts +3 -3
- package/src/common/real-time/event-models.ts +1 -1
- package/src/common/search/ActivitySearchSource.ts +2 -2
- package/src/common/search/FeedSearchSource.ts +2 -2
- package/src/common/search/UserSearchSource.ts +2 -2
- package/src/feed/event-handlers/activity/activity-marked-utils.test.ts +1 -1
- package/src/feed/event-handlers/activity/activity-reaction-utils.test.ts +1 -1
- package/src/feed/event-handlers/activity/activity-utils.test.ts +1 -1
- package/src/feed/event-handlers/activity/handle-activity-added.ts +3 -3
- package/src/feed/event-handlers/activity/handle-activity-marked.ts +3 -3
- package/src/feed/event-handlers/activity/handle-activity-pinned.test.ts +1 -1
- package/src/feed/event-handlers/activity/handle-activity-pinned.ts +3 -3
- package/src/feed/event-handlers/activity/handle-activity-reaction-added.test.ts +1 -1
- 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-removed-from-feed.ts +2 -2
- package/src/feed/event-handlers/activity/handle-activity-unpinned.test.ts +2 -2
- package/src/feed/event-handlers/activity/handle-activity-unpinned.ts +2 -2
- package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +2 -2
- package/src/feed/event-handlers/activity/handle-activity-updated.ts +3 -3
- package/src/feed/event-handlers/bookmark/bookmark-utils.test.ts +1 -1
- package/src/feed/event-handlers/comment/handle-comment-added.test.ts +2 -2
- package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +2 -2
- package/src/feed/event-handlers/comment/handle-comment-deleted.ts +3 -2
- package/src/feed/event-handlers/comment/handle-comment-reaction-added.test.ts +2 -2
- package/src/feed/event-handlers/comment/handle-comment-reaction-added.ts +3 -2
- package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.test.ts +2 -2
- package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.ts +3 -2
- package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +2 -2
- package/src/feed/event-handlers/comment/handle-comment-updated.ts +3 -2
- package/src/feed/event-handlers/comment/utils/update-comment-count.test.ts +3 -1
- package/src/feed/event-handlers/comment/utils/update-comment-count.ts +2 -2
- package/src/feed/event-handlers/feed/handle-feed-updated.ts +2 -2
- package/src/feed/event-handlers/feed-member/handle-feed-member-added.ts +2 -2
- package/src/feed/event-handlers/feed-member/handle-feed-member-removed.ts +2 -2
- package/src/feed/event-handlers/feed-member/handle-feed-member-updated.ts +2 -2
- package/src/feed/event-handlers/follow/handle-follow-created.test.ts +1 -1
- package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +1 -1
- package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +1 -1
- package/src/feed/event-handlers/follow/handle-follow-updated.ts +1 -1
- package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.test.ts +310 -12
- package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +90 -7
- package/src/feed/event-handlers/watch/handle-watch-started.ts +1 -1
- package/src/feed/event-handlers/watch/handle-watch-stopped.ts +1 -1
- package/src/feed/feed.test.ts +1 -1
- package/src/feed/feed.ts +24 -4
- package/src/feeds-client/event-handlers/user/handle-user-updated.test.ts +1 -1
- package/src/feeds-client/feeds-client.ts +5 -4
- package/src/gen/feeds/FeedApi.ts +2 -2
- package/src/gen/feeds/FeedsApi.ts +2 -2
- package/src/gen/model-decoders/decoders.ts +2 -4
- package/src/gen/model-decoders/event-decoder-mapping.ts +1 -1
- package/src/gen/models/index.ts +34 -33
- package/src/gen/moderation/ModerationApi.ts +2 -2
- package/src/test-utils/response-generators.ts +2 -2
- package/src/types-internal.ts +1 -1
- package/src/types.ts +4 -4
- package/src/utils/event-triggered-by-connected-user.ts +2 -2
- package/src/utils/state-update-queue.ts +2 -2
- package/src/utils/type-assertions.ts +7 -11
- package/dist/index-CFv0uza2.js.map +0 -1
- package/dist/index-DP0C8psw.mjs.map +0 -1
|
@@ -5,7 +5,7 @@ import * as commentHandlers from '../handle-comment-updated';
|
|
|
5
5
|
import * as activityHandlers from '../../activity';
|
|
6
6
|
import { FeedsClient } from '../../../../feeds-client';
|
|
7
7
|
import { Feed } from '../../../feed';
|
|
8
|
-
import { ActivityResponse, CommentResponse } from '../../../../gen/models';
|
|
8
|
+
import type { ActivityResponse, CommentResponse } from '../../../../gen/models';
|
|
9
9
|
import {
|
|
10
10
|
generateCommentResponse,
|
|
11
11
|
generateFeedResponse,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '../../../../test-utils';
|
|
15
15
|
|
|
16
16
|
vi.mock('../../activity', async (importOriginal) => {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
17
18
|
const actual = await importOriginal<typeof import('../../activity')>();
|
|
18
19
|
return {
|
|
19
20
|
...actual,
|
|
@@ -22,6 +23,7 @@ vi.mock('../../activity', async (importOriginal) => {
|
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
vi.mock('../handle-comment-updated', async (importOriginal) => {
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
25
27
|
const actual = await importOriginal<typeof import('../handle-comment-updated')>();
|
|
26
28
|
return {
|
|
27
29
|
...actual,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ActivityResponse, Feed } from '@self';
|
|
2
|
-
import { CommentResponse } from '@self';
|
|
1
|
+
import type { ActivityResponse, Feed } from '@self';
|
|
2
|
+
import type { CommentResponse } from '@self';
|
|
3
3
|
import { handleCommentUpdated } from '../handle-comment-updated';
|
|
4
4
|
import { handleActivityUpdated } from '../../activity';
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Feed } from '../../../feed';
|
|
2
|
-
import { EventPayload, PartializeAllBut } from '../../../types-internal';
|
|
1
|
+
import type { Feed } from '../../../feed';
|
|
2
|
+
import type { EventPayload, PartializeAllBut } from '../../../types-internal';
|
|
3
3
|
|
|
4
4
|
export function handleFeedUpdated(
|
|
5
5
|
this: Feed,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Feed, FeedState } from '../../../feed';
|
|
2
|
-
import { EventPayload } from '../../../types-internal';
|
|
1
|
+
import type { Feed, FeedState } from '../../../feed';
|
|
2
|
+
import type { EventPayload } from '../../../types-internal';
|
|
3
3
|
|
|
4
4
|
export function handleFeedMemberAdded(
|
|
5
5
|
this: Feed,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Feed, FeedState } from '../../../feed';
|
|
2
|
-
import { EventPayload } from '../../../types-internal';
|
|
1
|
+
import type { Feed, FeedState } from '../../../feed';
|
|
2
|
+
import type { EventPayload } from '../../../types-internal';
|
|
3
3
|
|
|
4
4
|
export function handleFeedMemberRemoved(
|
|
5
5
|
this: Feed,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Feed, FeedState } from '../../../feed';
|
|
2
|
-
import { EventPayload } from '../../../types-internal';
|
|
1
|
+
import type { Feed, FeedState } from '../../../feed';
|
|
2
|
+
import type { EventPayload } from '../../../types-internal';
|
|
3
3
|
|
|
4
4
|
export function handleFeedMemberUpdated(
|
|
5
5
|
this: Feed,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
getHumanId,
|
|
9
9
|
generateOwnUser,
|
|
10
10
|
} from '../../../test-utils/response-generators';
|
|
11
|
-
import { FollowResponse } from '../../../gen/models';
|
|
11
|
+
import type { FollowResponse } from '../../../gen/models';
|
|
12
12
|
|
|
13
13
|
describe(handleFollowUpdated.name, () => {
|
|
14
14
|
let feed: Feed;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Feed, FeedState } from '../../feed';
|
|
2
2
|
import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
|
|
3
|
-
import { EventPayload, PartializeAllBut } from '../../../types-internal';
|
|
3
|
+
import type { EventPayload, PartializeAllBut } from '../../../types-internal';
|
|
4
4
|
|
|
5
5
|
export function handleFollowUpdated(
|
|
6
6
|
this: Feed,
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import type {
|
|
3
3
|
NotificationFeedUpdatedEvent,
|
|
4
4
|
NotificationStatusResponse,
|
|
5
5
|
AggregatedActivityResponse,
|
|
6
6
|
} from '../../../gen/models';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
updateNotificationFeedFromEvent,
|
|
9
|
+
addAggregatedActivitiesToState,
|
|
10
|
+
updateNotificationStatus,
|
|
11
|
+
} from './handle-notification-feed-updated';
|
|
8
12
|
|
|
9
13
|
const createMockNotificationFeedUpdatedEvent = (
|
|
10
14
|
overrides: Partial<NotificationFeedUpdatedEvent> = {},
|
|
@@ -30,6 +34,7 @@ const createMockAggregatedActivity = (
|
|
|
30
34
|
activity_count: 1,
|
|
31
35
|
created_at: new Date(),
|
|
32
36
|
group: 'test-group',
|
|
37
|
+
user_count_truncated: false,
|
|
33
38
|
score: 1,
|
|
34
39
|
updated_at: new Date(),
|
|
35
40
|
user_count: 1,
|
|
@@ -42,7 +47,34 @@ describe('notification-feed-utils', () => {
|
|
|
42
47
|
it('should return unchanged if event has no notification_status or aggregated_activities', () => {
|
|
43
48
|
const event = createMockNotificationFeedUpdatedEvent();
|
|
44
49
|
|
|
45
|
-
const result = updateNotificationFeedFromEvent(event
|
|
50
|
+
const result = updateNotificationFeedFromEvent(event, [], {
|
|
51
|
+
unread: 0,
|
|
52
|
+
unseen: 0,
|
|
53
|
+
read_activities: [],
|
|
54
|
+
seen_activities: [],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(result.changed).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it(`shouldn't update notification_status when event has notification_status but currentNotificationStatus is undefined`, () => {
|
|
61
|
+
const event = createMockNotificationFeedUpdatedEvent({
|
|
62
|
+
notification_status: createMockNotificationStatus(),
|
|
63
|
+
});
|
|
64
|
+
const result = updateNotificationFeedFromEvent(event, [], undefined);
|
|
65
|
+
|
|
66
|
+
expect(result.changed).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it(`shouldn't update aggregated_activities when event has aggregated_activities but currentAggregatedActivities is undefined`, () => {
|
|
70
|
+
const event = createMockNotificationFeedUpdatedEvent({
|
|
71
|
+
aggregated_activities: [createMockAggregatedActivity()],
|
|
72
|
+
});
|
|
73
|
+
const result = updateNotificationFeedFromEvent(
|
|
74
|
+
event,
|
|
75
|
+
undefined,
|
|
76
|
+
createMockNotificationStatus(),
|
|
77
|
+
);
|
|
46
78
|
|
|
47
79
|
expect(result.changed).toBe(false);
|
|
48
80
|
});
|
|
@@ -52,15 +84,23 @@ describe('notification-feed-utils', () => {
|
|
|
52
84
|
unread: 5,
|
|
53
85
|
unseen: 3,
|
|
54
86
|
read_activities: ['activity1', 'activity2'],
|
|
87
|
+
seen_activities: [],
|
|
55
88
|
});
|
|
56
89
|
const event = createMockNotificationFeedUpdatedEvent({
|
|
57
90
|
notification_status: notificationStatus,
|
|
58
91
|
});
|
|
59
92
|
|
|
60
|
-
const result = updateNotificationFeedFromEvent(event
|
|
93
|
+
const result = updateNotificationFeedFromEvent(event, [], {
|
|
94
|
+
unread: 0,
|
|
95
|
+
unseen: 0,
|
|
96
|
+
read_activities: [],
|
|
97
|
+
seen_activities: [],
|
|
98
|
+
});
|
|
61
99
|
|
|
62
100
|
expect(result.changed).toBe(true);
|
|
63
|
-
expect(result.data?.notification_status).
|
|
101
|
+
expect(result.data?.notification_status).toStrictEqual(
|
|
102
|
+
notificationStatus,
|
|
103
|
+
);
|
|
64
104
|
expect(result.data?.aggregated_activities).toBeUndefined();
|
|
65
105
|
});
|
|
66
106
|
|
|
@@ -73,10 +113,17 @@ describe('notification-feed-utils', () => {
|
|
|
73
113
|
aggregated_activities: aggregatedActivities,
|
|
74
114
|
});
|
|
75
115
|
|
|
76
|
-
const result = updateNotificationFeedFromEvent(event
|
|
116
|
+
const result = updateNotificationFeedFromEvent(event, [], {
|
|
117
|
+
unread: 0,
|
|
118
|
+
unseen: 0,
|
|
119
|
+
read_activities: [],
|
|
120
|
+
seen_activities: [],
|
|
121
|
+
});
|
|
77
122
|
|
|
78
123
|
expect(result.changed).toBe(true);
|
|
79
|
-
expect(result.data?.aggregated_activities).
|
|
124
|
+
expect(result.data?.aggregated_activities).toStrictEqual(
|
|
125
|
+
aggregatedActivities,
|
|
126
|
+
);
|
|
80
127
|
expect(result.data?.notification_status).toBeUndefined();
|
|
81
128
|
});
|
|
82
129
|
|
|
@@ -93,11 +140,23 @@ describe('notification-feed-utils', () => {
|
|
|
93
140
|
aggregated_activities: aggregatedActivities,
|
|
94
141
|
});
|
|
95
142
|
|
|
96
|
-
const result = updateNotificationFeedFromEvent(event
|
|
143
|
+
const result = updateNotificationFeedFromEvent(event, [], {
|
|
144
|
+
unread: 0,
|
|
145
|
+
unseen: 0,
|
|
146
|
+
read_activities: [],
|
|
147
|
+
seen_activities: [],
|
|
148
|
+
});
|
|
97
149
|
|
|
98
150
|
expect(result.changed).toBe(true);
|
|
99
|
-
expect(result.data?.notification_status).toBe(
|
|
100
|
-
|
|
151
|
+
expect(result.data?.notification_status?.unread).toBe(
|
|
152
|
+
notificationStatus.unread,
|
|
153
|
+
);
|
|
154
|
+
expect(result.data?.notification_status?.unseen).toBe(
|
|
155
|
+
notificationStatus.unseen,
|
|
156
|
+
);
|
|
157
|
+
expect(result.data?.aggregated_activities).toStrictEqual(
|
|
158
|
+
aggregatedActivities,
|
|
159
|
+
);
|
|
101
160
|
});
|
|
102
161
|
|
|
103
162
|
it('should handle notification_status with all fields', () => {
|
|
@@ -105,16 +164,255 @@ describe('notification-feed-utils', () => {
|
|
|
105
164
|
unread: 10,
|
|
106
165
|
unseen: 5,
|
|
107
166
|
last_seen_at: new Date('2023-01-01'),
|
|
167
|
+
seen_activities: [],
|
|
108
168
|
read_activities: ['activity1', 'activity2', 'activity3'],
|
|
109
169
|
});
|
|
110
170
|
const event = createMockNotificationFeedUpdatedEvent({
|
|
111
171
|
notification_status: notificationStatus,
|
|
112
172
|
});
|
|
113
173
|
|
|
114
|
-
const result = updateNotificationFeedFromEvent(event
|
|
174
|
+
const result = updateNotificationFeedFromEvent(event, [], {
|
|
175
|
+
unread: 0,
|
|
176
|
+
unseen: 0,
|
|
177
|
+
read_activities: [],
|
|
178
|
+
seen_activities: [],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
expect(result.changed).toBe(true);
|
|
182
|
+
expect(result.data?.notification_status).toStrictEqual(
|
|
183
|
+
notificationStatus,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('addAggregatedActivitiesToState', () => {
|
|
189
|
+
it('should add new activities when none exist', () => {
|
|
190
|
+
const newActivities = [
|
|
191
|
+
createMockAggregatedActivity({ group: 'group1' }),
|
|
192
|
+
createMockAggregatedActivity({ group: 'group2' }),
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
const result = addAggregatedActivitiesToState(
|
|
196
|
+
newActivities,
|
|
197
|
+
undefined,
|
|
198
|
+
'start',
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect(result.changed).toBe(true);
|
|
202
|
+
expect(result.aggregated_activities).toStrictEqual(newActivities);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should add new activities to existing ones', () => {
|
|
206
|
+
const existingActivities = [
|
|
207
|
+
createMockAggregatedActivity({ group: 'existing1' }),
|
|
208
|
+
];
|
|
209
|
+
const newActivities = [
|
|
210
|
+
createMockAggregatedActivity({ group: 'new1' }),
|
|
211
|
+
createMockAggregatedActivity({ group: 'new2' }),
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
const result = addAggregatedActivitiesToState(
|
|
215
|
+
newActivities,
|
|
216
|
+
existingActivities,
|
|
217
|
+
'start',
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
expect(result.changed).toBe(true);
|
|
221
|
+
expect(result.aggregated_activities).toStrictEqual([
|
|
222
|
+
...newActivities,
|
|
223
|
+
...existingActivities,
|
|
224
|
+
]);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should add new activities at the end when position is end', () => {
|
|
228
|
+
const existingActivities = [
|
|
229
|
+
createMockAggregatedActivity({ group: 'existing1' }),
|
|
230
|
+
];
|
|
231
|
+
const newActivities = [createMockAggregatedActivity({ group: 'new1' })];
|
|
232
|
+
|
|
233
|
+
const result = addAggregatedActivitiesToState(
|
|
234
|
+
newActivities,
|
|
235
|
+
existingActivities,
|
|
236
|
+
'end',
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
expect(result.changed).toBe(true);
|
|
240
|
+
expect(result.aggregated_activities).toStrictEqual([
|
|
241
|
+
...existingActivities,
|
|
242
|
+
...newActivities,
|
|
243
|
+
]);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should update existing activities with same group (upsert)', () => {
|
|
247
|
+
const baseDate = new Date('2023-01-01');
|
|
248
|
+
const existingActivities = [
|
|
249
|
+
createMockAggregatedActivity({
|
|
250
|
+
group: 'group1',
|
|
251
|
+
activity_count: 1,
|
|
252
|
+
score: 10,
|
|
253
|
+
updated_at: baseDate,
|
|
254
|
+
}),
|
|
255
|
+
createMockAggregatedActivity({
|
|
256
|
+
group: 'group2',
|
|
257
|
+
activity_count: 2,
|
|
258
|
+
score: 20,
|
|
259
|
+
}),
|
|
260
|
+
];
|
|
261
|
+
const newActivities = [
|
|
262
|
+
createMockAggregatedActivity({
|
|
263
|
+
group: 'group1',
|
|
264
|
+
activity_count: 3,
|
|
265
|
+
score: 30,
|
|
266
|
+
updated_at: new Date('2023-01-02'),
|
|
267
|
+
}),
|
|
268
|
+
createMockAggregatedActivity({
|
|
269
|
+
group: 'group3',
|
|
270
|
+
activity_count: 4,
|
|
271
|
+
score: 40,
|
|
272
|
+
}),
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
const result = addAggregatedActivitiesToState(
|
|
276
|
+
newActivities,
|
|
277
|
+
existingActivities,
|
|
278
|
+
'start',
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(result.changed).toBe(true);
|
|
282
|
+
expect(result.aggregated_activities).toHaveLength(3);
|
|
283
|
+
|
|
284
|
+
// Check that group1 was updated
|
|
285
|
+
const updatedGroup1 = result.aggregated_activities.find(
|
|
286
|
+
(a) => a.group === 'group1',
|
|
287
|
+
);
|
|
288
|
+
expect(updatedGroup1?.activity_count).toBe(3);
|
|
289
|
+
expect(updatedGroup1?.score).toBe(30);
|
|
290
|
+
expect(updatedGroup1?.updated_at).toEqual(new Date('2023-01-02'));
|
|
291
|
+
|
|
292
|
+
// Check that group2 remains unchanged
|
|
293
|
+
const unchangedGroup2 = result.aggregated_activities.find(
|
|
294
|
+
(a) => a.group === 'group2',
|
|
295
|
+
);
|
|
296
|
+
expect(unchangedGroup2?.activity_count).toBe(2);
|
|
297
|
+
expect(unchangedGroup2?.score).toBe(20);
|
|
298
|
+
|
|
299
|
+
// Check that group3 was added
|
|
300
|
+
const newGroup3 = result.aggregated_activities.find(
|
|
301
|
+
(a) => a.group === 'group3',
|
|
302
|
+
);
|
|
303
|
+
expect(newGroup3?.activity_count).toBe(4);
|
|
304
|
+
expect(newGroup3?.score).toBe(40);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should handle mixed new and existing activities', () => {
|
|
308
|
+
const existingActivities = [
|
|
309
|
+
createMockAggregatedActivity({ group: 'existing1' }),
|
|
310
|
+
createMockAggregatedActivity({ group: 'existing2' }),
|
|
311
|
+
];
|
|
312
|
+
const newActivities = [
|
|
313
|
+
createMockAggregatedActivity({ group: 'existing1', activity_count: 5 }), // Update existing
|
|
314
|
+
createMockAggregatedActivity({ group: 'new1' }), // Add new
|
|
315
|
+
createMockAggregatedActivity({ group: 'existing2', score: 100 }), // Update existing
|
|
316
|
+
createMockAggregatedActivity({ group: 'new2' }), // Add new
|
|
317
|
+
];
|
|
318
|
+
|
|
319
|
+
const result = addAggregatedActivitiesToState(
|
|
320
|
+
newActivities,
|
|
321
|
+
existingActivities,
|
|
322
|
+
'start',
|
|
323
|
+
);
|
|
115
324
|
|
|
116
325
|
expect(result.changed).toBe(true);
|
|
117
|
-
expect(result.
|
|
326
|
+
expect(result.aggregated_activities).toHaveLength(4);
|
|
327
|
+
|
|
328
|
+
// Check that existing1 was updated
|
|
329
|
+
const updatedExisting1 = result.aggregated_activities.find(
|
|
330
|
+
(a) => a.group === 'existing1',
|
|
331
|
+
);
|
|
332
|
+
expect(updatedExisting1?.activity_count).toBe(5);
|
|
333
|
+
|
|
334
|
+
// Check that existing2 was updated
|
|
335
|
+
const updatedExisting2 = result.aggregated_activities.find(
|
|
336
|
+
(a) => a.group === 'existing2',
|
|
337
|
+
);
|
|
338
|
+
expect(updatedExisting2?.score).toBe(100);
|
|
339
|
+
|
|
340
|
+
// Check that new activities were added
|
|
341
|
+
expect(
|
|
342
|
+
result.aggregated_activities.find((a) => a.group === 'new1'),
|
|
343
|
+
).toBeDefined();
|
|
344
|
+
expect(
|
|
345
|
+
result.aggregated_activities.find((a) => a.group === 'new2'),
|
|
346
|
+
).toBeDefined();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('should preserve order when adding at start', () => {
|
|
350
|
+
const existingActivities = [
|
|
351
|
+
createMockAggregatedActivity({ group: 'existing1' }),
|
|
352
|
+
createMockAggregatedActivity({ group: 'existing2' }),
|
|
353
|
+
];
|
|
354
|
+
const newActivities = [
|
|
355
|
+
createMockAggregatedActivity({ group: 'new1' }),
|
|
356
|
+
createMockAggregatedActivity({ group: 'new2' }),
|
|
357
|
+
];
|
|
358
|
+
|
|
359
|
+
const result = addAggregatedActivitiesToState(
|
|
360
|
+
newActivities,
|
|
361
|
+
existingActivities,
|
|
362
|
+
'start',
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
expect(result.aggregated_activities).toStrictEqual([
|
|
366
|
+
...newActivities,
|
|
367
|
+
...existingActivities,
|
|
368
|
+
]);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should preserve order when adding at end', () => {
|
|
372
|
+
const existingActivities = [
|
|
373
|
+
createMockAggregatedActivity({ group: 'existing1' }),
|
|
374
|
+
createMockAggregatedActivity({ group: 'existing2' }),
|
|
375
|
+
];
|
|
376
|
+
const newActivities = [
|
|
377
|
+
createMockAggregatedActivity({ group: 'new1' }),
|
|
378
|
+
createMockAggregatedActivity({ group: 'new2' }),
|
|
379
|
+
];
|
|
380
|
+
|
|
381
|
+
const result = addAggregatedActivitiesToState(
|
|
382
|
+
newActivities,
|
|
383
|
+
existingActivities,
|
|
384
|
+
'end',
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
expect(result.aggregated_activities).toStrictEqual([
|
|
388
|
+
...existingActivities,
|
|
389
|
+
...newActivities,
|
|
390
|
+
]);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
describe('updateNotificationStatus', () => {
|
|
395
|
+
it('should replace old state with new one', () => {
|
|
396
|
+
const newNotificationStatus = createMockNotificationStatus({
|
|
397
|
+
unread: 5,
|
|
398
|
+
unseen: 3,
|
|
399
|
+
read_activities: ['activity1', 'activity2'],
|
|
400
|
+
seen_activities: ['activity3', 'activity4'],
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const currentNotificationStatus = createMockNotificationStatus({
|
|
404
|
+
unread: 2,
|
|
405
|
+
unseen: 1,
|
|
406
|
+
read_activities: ['activity5', 'activity6'],
|
|
407
|
+
seen_activities: ['activity7', 'activity8'],
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const result = updateNotificationStatus(
|
|
411
|
+
newNotificationStatus,
|
|
412
|
+
currentNotificationStatus,
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
expect(result.notification_status).toStrictEqual(newNotificationStatus);
|
|
118
416
|
});
|
|
119
417
|
});
|
|
120
418
|
});
|
|
@@ -1,13 +1,76 @@
|
|
|
1
1
|
import type { Feed } from '../../../feed';
|
|
2
|
-
import {
|
|
2
|
+
import type {
|
|
3
3
|
AggregatedActivityResponse,
|
|
4
4
|
NotificationFeedUpdatedEvent,
|
|
5
5
|
NotificationStatusResponse,
|
|
6
6
|
} from '../../../gen/models';
|
|
7
7
|
import type { EventPayload, UpdateStateResult } from '../../../types-internal';
|
|
8
|
+
import { uniqueArrayMerge } from '../../../utils';
|
|
9
|
+
|
|
10
|
+
export const addAggregatedActivitiesToState = (
|
|
11
|
+
newAggregatedActivities: AggregatedActivityResponse[],
|
|
12
|
+
aggregatedActivities: AggregatedActivityResponse[] | undefined,
|
|
13
|
+
position: 'start' | 'end',
|
|
14
|
+
) => {
|
|
15
|
+
let result: UpdateStateResult<{
|
|
16
|
+
aggregated_activities: AggregatedActivityResponse[];
|
|
17
|
+
}>;
|
|
18
|
+
if (newAggregatedActivities.length === 0) {
|
|
19
|
+
result = {
|
|
20
|
+
changed: false,
|
|
21
|
+
aggregated_activities: [],
|
|
22
|
+
};
|
|
23
|
+
} else {
|
|
24
|
+
result = {
|
|
25
|
+
changed: true,
|
|
26
|
+
aggregated_activities: [],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
result.aggregated_activities =
|
|
31
|
+
position === 'start'
|
|
32
|
+
? uniqueArrayMerge(
|
|
33
|
+
newAggregatedActivities,
|
|
34
|
+
aggregatedActivities ?? [],
|
|
35
|
+
(a) => a.group,
|
|
36
|
+
)
|
|
37
|
+
: uniqueArrayMerge(
|
|
38
|
+
aggregatedActivities ?? [],
|
|
39
|
+
newAggregatedActivities,
|
|
40
|
+
(a) => a.group,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const updateNotificationStatus = (
|
|
47
|
+
newNotificationStatus?: NotificationStatusResponse,
|
|
48
|
+
currentNotificationStatus?: NotificationStatusResponse,
|
|
49
|
+
) => {
|
|
50
|
+
if (!newNotificationStatus && !currentNotificationStatus) {
|
|
51
|
+
return {
|
|
52
|
+
changed: false,
|
|
53
|
+
notification_status: undefined,
|
|
54
|
+
};
|
|
55
|
+
} else if (!newNotificationStatus) {
|
|
56
|
+
return {
|
|
57
|
+
changed: false,
|
|
58
|
+
notification_status: currentNotificationStatus,
|
|
59
|
+
};
|
|
60
|
+
} else {
|
|
61
|
+
return {
|
|
62
|
+
changed: true,
|
|
63
|
+
notification_status: {
|
|
64
|
+
...newNotificationStatus,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
};
|
|
8
69
|
|
|
9
70
|
export const updateNotificationFeedFromEvent = (
|
|
10
71
|
event: NotificationFeedUpdatedEvent,
|
|
72
|
+
currentAggregatedActivities?: AggregatedActivityResponse[],
|
|
73
|
+
currentNotificationStatus?: NotificationStatusResponse,
|
|
11
74
|
): UpdateStateResult<{
|
|
12
75
|
data?: {
|
|
13
76
|
notification_status?: NotificationStatusResponse;
|
|
@@ -19,15 +82,31 @@ export const updateNotificationFeedFromEvent = (
|
|
|
19
82
|
aggregated_activities?: AggregatedActivityResponse[];
|
|
20
83
|
} = {};
|
|
21
84
|
|
|
22
|
-
if (event.notification_status) {
|
|
23
|
-
|
|
85
|
+
if (event.notification_status && currentNotificationStatus) {
|
|
86
|
+
const notificationStatusResult = updateNotificationStatus(
|
|
87
|
+
event.notification_status,
|
|
88
|
+
currentNotificationStatus,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (notificationStatusResult.changed) {
|
|
92
|
+
updates.notification_status =
|
|
93
|
+
notificationStatusResult.notification_status;
|
|
94
|
+
}
|
|
24
95
|
}
|
|
25
96
|
|
|
26
|
-
if (event.aggregated_activities) {
|
|
27
|
-
|
|
97
|
+
if (event.aggregated_activities && currentAggregatedActivities) {
|
|
98
|
+
const aggregatedActivitiesResult = addAggregatedActivitiesToState(
|
|
99
|
+
event.aggregated_activities,
|
|
100
|
+
currentAggregatedActivities,
|
|
101
|
+
'start',
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (aggregatedActivitiesResult.changed) {
|
|
105
|
+
updates.aggregated_activities =
|
|
106
|
+
aggregatedActivitiesResult.aggregated_activities;
|
|
107
|
+
}
|
|
28
108
|
}
|
|
29
109
|
|
|
30
|
-
// Only return changed if we have actual updates
|
|
31
110
|
if (Object.keys(updates).length > 0) {
|
|
32
111
|
return {
|
|
33
112
|
changed: true,
|
|
@@ -44,7 +123,11 @@ export function handleNotificationFeedUpdated(
|
|
|
44
123
|
this: Feed,
|
|
45
124
|
event: EventPayload<'feeds.notification_feed.updated'>,
|
|
46
125
|
) {
|
|
47
|
-
const result = updateNotificationFeedFromEvent(
|
|
126
|
+
const result = updateNotificationFeedFromEvent(
|
|
127
|
+
event,
|
|
128
|
+
this.currentState.aggregated_activities,
|
|
129
|
+
this.currentState.notification_status,
|
|
130
|
+
);
|
|
48
131
|
if (result.changed) {
|
|
49
132
|
this.state.partialNext({
|
|
50
133
|
notification_status: result.data?.notification_status,
|
package/src/feed/feed.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
2
|
import { FeedsClient } from '../feeds-client';
|
|
3
3
|
import { Feed } from './feed';
|
|
4
|
-
import { ActivityResponse } from '../gen/models';
|
|
4
|
+
import type { ActivityResponse } from '../gen/models';
|
|
5
5
|
import { generateActivityResponse, generateFeedResponse } from '../test-utils';
|
|
6
6
|
|
|
7
7
|
describe('Feed derived state updates', () => {
|