@stream-io/feeds-client 0.2.12 → 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 +7 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/react-bindings.js +22 -14
- package/dist/cjs/react-bindings.js.map +1 -1
- package/dist/es/index.mjs +2 -2
- package/dist/es/react-bindings.mjs +22 -14
- package/dist/es/react-bindings.mjs.map +1 -1
- package/dist/{index-o7AeSkxa.js → index-RzB4c4g6.js} +81 -9
- package/dist/index-RzB4c4g6.js.map +1 -0
- package/dist/{index-D7QtnkUs.mjs → index-gvcJhGPH.mjs} +81 -9
- 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/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +8 -1
- package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map +1 -1
- package/dist/types/feed/feed.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/feed/event-handlers/notification-feed/handle-notification-feed-updated.test.ts +309 -11
- package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +89 -6
- package/src/feed/feed.ts +21 -1
- package/dist/index-D7QtnkUs.mjs.map +0 -1
- package/dist/index-o7AeSkxa.js.map +0 -1
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { Feed, FeedState } from '@self';
|
|
2
|
-
declare const selector: ({ aggregated_activities }: FeedState) => {
|
|
3
|
-
|
|
2
|
+
declare const selector: ({ is_loading_activities, next, aggregated_activities, }: FeedState) => {
|
|
3
|
+
is_loading: boolean;
|
|
4
|
+
has_next_page: boolean;
|
|
5
|
+
aggregated_activities: import("@self").AggregatedActivityResponse[];
|
|
6
|
+
};
|
|
7
|
+
type UseAggregatedActivitiesReturnType = ReturnType<typeof selector> & {
|
|
8
|
+
loadNextPage: () => Promise<void>;
|
|
4
9
|
};
|
|
5
|
-
type UseAggregatedActivitiesReturnType = ReturnType<typeof selector>;
|
|
6
10
|
/**
|
|
7
|
-
* A React hook that returns a reactive object containing the current aggregated activities
|
|
11
|
+
* A React hook that returns a reactive object containing the current aggregated activities,
|
|
12
|
+
* loading state and whether there is a next page to paginate to or not.
|
|
8
13
|
*/
|
|
9
14
|
export declare function useAggregatedActivities(feedFromProps: Feed): UseAggregatedActivitiesReturnType;
|
|
10
15
|
export declare function useAggregatedActivities(feedFromProps?: Feed): UseAggregatedActivitiesReturnType | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAggregatedActivities.d.ts","sourceRoot":"","sources":["../../../../../../src/bindings/react/hooks/feed-state-hooks/useAggregatedActivities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useAggregatedActivities.d.ts","sourceRoot":"","sources":["../../../../../../src/bindings/react/hooks/feed-state-hooks/useAggregatedActivities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAM7C,QAAA,MAAM,QAAQ,GAAI,yDAIf,SAAS;;;;CAIV,CAAC;AAEH,KAAK,iCAAiC,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,GAAG;IACrE,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,IAAI,GAClB,iCAAiC,CAAC;AACrC,wBAAgB,uBAAuB,CACrC,aAAa,CAAC,EAAE,IAAI,GACnB,iCAAiC,GAAG,SAAS,CAAC"}
|
package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIsAggregatedActivityRead.d.ts","sourceRoot":"","sources":["../../../../../../src/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAE9D,eAAO,MAAM,2BAA2B,GAAI,8CAGzC;IACD,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,kBAAkB,EAAE,0BAA0B,CAAC;CAChD,
|
|
1
|
+
{"version":3,"file":"useIsAggregatedActivityRead.d.ts","sourceRoot":"","sources":["../../../../../../src/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAE9D,eAAO,MAAM,2BAA2B,GAAI,8CAGzC;IACD,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,kBAAkB,EAAE,0BAA0B,CAAC;CAChD,YAgBA,CAAC"}
|
package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { Feed } from '../../../feed';
|
|
2
2
|
import type { AggregatedActivityResponse, NotificationFeedUpdatedEvent, NotificationStatusResponse } from '../../../gen/models';
|
|
3
3
|
import type { EventPayload, UpdateStateResult } from '../../../types-internal';
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const addAggregatedActivitiesToState: (newAggregatedActivities: AggregatedActivityResponse[], aggregatedActivities: AggregatedActivityResponse[] | undefined, position: "start" | "end") => UpdateStateResult<{
|
|
5
|
+
aggregated_activities: AggregatedActivityResponse[];
|
|
6
|
+
}>;
|
|
7
|
+
export declare const updateNotificationStatus: (newNotificationStatus?: NotificationStatusResponse, currentNotificationStatus?: NotificationStatusResponse) => {
|
|
8
|
+
changed: boolean;
|
|
9
|
+
notification_status: NotificationStatusResponse | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare const updateNotificationFeedFromEvent: (event: NotificationFeedUpdatedEvent, currentAggregatedActivities?: AggregatedActivityResponse[], currentNotificationStatus?: NotificationStatusResponse) => UpdateStateResult<{
|
|
5
12
|
data?: {
|
|
6
13
|
notification_status?: NotificationStatusResponse;
|
|
7
14
|
aggregated_activities?: AggregatedActivityResponse[];
|
package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-notification-feed-updated.d.ts","sourceRoot":"","sources":["../../../../../src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,EAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"handle-notification-feed-updated.d.ts","sourceRoot":"","sources":["../../../../../src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,EAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG/E,eAAO,MAAM,8BAA8B,GACzC,yBAAyB,0BAA0B,EAAE,EACrD,sBAAsB,0BAA0B,EAAE,GAAG,SAAS,EAC9D,UAAU,OAAO,GAAG,KAAK;2BAGA,0BAA0B,EAAE;EA4BtD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,wBAAwB,0BAA0B,EAClD,4BAA4B,0BAA0B;;;CAoBvD,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,OAAO,4BAA4B,EACnC,8BAA8B,0BAA0B,EAAE,EAC1D,4BAA4B,0BAA0B,KACrD,iBAAiB,CAAC;IACnB,IAAI,CAAC,EAAE;QACL,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;QACjD,qBAAqB,CAAC,EAAE,0BAA0B,EAAE,CAAC;KACtD,CAAC;CACH,CAyCA,CAAC;AAEF,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,YAAY,CAAC,iCAAiC,CAAC,QAavD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../../../src/feed/feed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,OAAO,EACP,gBAAgB,EAChB,eAAe,EAEf,uBAAuB,EACvB,gBAAgB,EAEhB,aAAa,EACb,oBAAoB,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../../../src/feed/feed.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import type { Feed, FeedState } from '@self';
|
|
2
2
|
import { useStateStore } from '@stream-io/state-store/react-bindings';
|
|
3
3
|
import { useFeedContext } from '../../contexts/StreamFeedContext';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { useStableCallback } from '../internal';
|
|
4
6
|
|
|
5
|
-
const selector = ({
|
|
7
|
+
const selector = ({
|
|
8
|
+
is_loading_activities,
|
|
9
|
+
next,
|
|
10
|
+
aggregated_activities = [],
|
|
11
|
+
}: FeedState) => ({
|
|
12
|
+
is_loading: is_loading_activities,
|
|
13
|
+
has_next_page: typeof next !== 'undefined',
|
|
6
14
|
aggregated_activities,
|
|
7
15
|
});
|
|
8
16
|
|
|
9
|
-
type UseAggregatedActivitiesReturnType = ReturnType<typeof selector
|
|
17
|
+
type UseAggregatedActivitiesReturnType = ReturnType<typeof selector> & {
|
|
18
|
+
loadNextPage: () => Promise<void>;
|
|
19
|
+
};
|
|
10
20
|
|
|
11
21
|
/**
|
|
12
|
-
* A React hook that returns a reactive object containing the current aggregated activities
|
|
22
|
+
* A React hook that returns a reactive object containing the current aggregated activities,
|
|
23
|
+
* loading state and whether there is a next page to paginate to or not.
|
|
13
24
|
*/
|
|
14
25
|
export function useAggregatedActivities(
|
|
15
26
|
feedFromProps: Feed,
|
|
@@ -21,5 +32,18 @@ export function useAggregatedActivities(feedFromProps?: Feed) {
|
|
|
21
32
|
const feedFromContext = useFeedContext();
|
|
22
33
|
const feed = feedFromProps ?? feedFromContext;
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
const data = useStateStore(feed?.state, selector);
|
|
36
|
+
|
|
37
|
+
const loadNextPage = useStableCallback(async () => {
|
|
38
|
+
if (!feed || !data?.has_next_page || data?.is_loading) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await feed.getNextPage();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return useMemo(
|
|
46
|
+
() => (data ? { ...data, loadNextPage } : undefined),
|
|
47
|
+
[data, loadNextPage],
|
|
48
|
+
);
|
|
25
49
|
}
|
|
@@ -13,17 +13,16 @@ export const useIsAggregatedActivityRead = ({
|
|
|
13
13
|
const feedFromContext = useFeedContext();
|
|
14
14
|
const feed = feedFromProps ?? feedFromContext;
|
|
15
15
|
|
|
16
|
-
const { read_activities: readActivities
|
|
16
|
+
const { read_activities: readActivities, last_read_at: lastReadAt } =
|
|
17
17
|
useNotificationStatus(feed) ?? {};
|
|
18
18
|
|
|
19
19
|
const group = aggregatedActivity.group;
|
|
20
20
|
|
|
21
21
|
return useMemo(
|
|
22
22
|
() =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// aggregatedActivity.updated_at.getTime() <= lastReadAt.getTime()) ||
|
|
23
|
+
(lastReadAt &&
|
|
24
|
+
aggregatedActivity.updated_at.getTime() <= lastReadAt.getTime()) ||
|
|
26
25
|
(readActivities ?? []).includes(group),
|
|
27
|
-
[readActivities, group],
|
|
26
|
+
[lastReadAt, aggregatedActivity.updated_at, readActivities, group],
|
|
28
27
|
);
|
|
29
28
|
};
|
|
@@ -4,7 +4,11 @@ import type {
|
|
|
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
|
+
);
|
|
115
280
|
|
|
116
281
|
expect(result.changed).toBe(true);
|
|
117
|
-
expect(result.
|
|
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
|
+
);
|
|
324
|
+
|
|
325
|
+
expect(result.changed).toBe(true);
|
|
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
|
});
|
|
@@ -5,9 +5,72 @@ import type {
|
|
|
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,
|