@stream-io/feeds-client 0.2.17 → 0.2.19
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 +24 -0
- package/dist/cjs/index.js +94 -25
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react-bindings.js +26 -55
- package/dist/cjs/react-bindings.js.map +1 -1
- package/dist/es/index.mjs +86 -17
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/react-bindings.mjs +19 -48
- package/dist/es/react-bindings.mjs.map +1 -1
- package/dist/{index-nq6SDtbt.js → feeds-client-C09giTf1.js} +322 -133
- package/dist/feeds-client-C09giTf1.js.map +1 -0
- package/dist/{index-BZL77zNq.mjs → feeds-client-CFadXO-B.mjs} +335 -146
- package/dist/feeds-client-CFadXO-B.mjs.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts +2 -32
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts.map +1 -1
- package/dist/types/common/real-time/StableWSConnection.d.ts +3 -3
- package/dist/types/common/real-time/event-models.d.ts +7 -2
- package/dist/types/common/real-time/event-models.d.ts.map +1 -1
- package/dist/types/common/types.d.ts +1 -0
- package/dist/types/common/types.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts +4 -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-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity-updater.d.ts +44 -0
- package/dist/types/feed/event-handlers/activity-updater.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts +6 -0
- package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/index.d.ts +3 -1
- package/dist/types/feed/event-handlers/index.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/{aggregated-feed/handle-aggregated-feed-updated.d.ts → notification-feed/handle-notification-feed-updated.d.ts} +2 -11
- package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/notification-feed/index.d.ts +2 -0
- package/dist/types/feed/event-handlers/notification-feed/index.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/story-feeds/handle-story-feeds-updated.d.ts +15 -0
- package/dist/types/feed/event-handlers/story-feeds/handle-story-feeds-updated.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/story-feeds/index.d.ts +2 -0
- package/dist/types/feed/event-handlers/story-feeds/index.d.ts.map +1 -0
- package/dist/types/feed/feed.d.ts +10 -4
- package/dist/types/feed/feed.d.ts.map +1 -1
- package/dist/types/feeds-client/feeds-client.d.ts +14 -4
- package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
- package/dist/types/gen/feeds/FeedsApi.d.ts.map +1 -1
- package/dist/types/gen/models/index.d.ts +42 -451
- package/dist/types/gen/models/index.d.ts.map +1 -1
- package/dist/types/utils/throttling/index.d.ts +3 -0
- package/dist/types/utils/throttling/index.d.ts.map +1 -0
- package/dist/types/utils/throttling/throttle.d.ts +34 -0
- package/dist/types/utils/throttling/throttle.d.ts.map +1 -0
- package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts +14 -0
- package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts.map +1 -0
- package/package.json +7 -3
- package/react-bindings.d.ts +11 -0
- package/react-bindings.js +7 -0
- package/react-bindings.mjs +11 -0
- package/src/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.ts +21 -73
- package/src/common/real-time/event-models.ts +8 -2
- package/src/common/types.ts +1 -0
- package/src/feed/event-handlers/activity/handle-activity-added.ts +18 -12
- package/src/feed/event-handlers/activity/handle-activity-updated.ts +12 -16
- package/src/feed/event-handlers/activity-updater.ts +15 -0
- package/src/feed/event-handlers/add-aggregated-activities-to-state.ts +72 -0
- package/src/feed/event-handlers/index.ts +3 -1
- package/src/feed/event-handlers/{aggregated-feed/handle-aggregated-feed-updated.ts → notification-feed/handle-notification-feed-updated.ts} +2 -94
- package/src/feed/event-handlers/notification-feed/index.ts +1 -0
- package/src/feed/event-handlers/story-feeds/handle-story-feeds-updated.ts +122 -0
- package/src/feed/event-handlers/story-feeds/index.ts +1 -0
- package/src/feed/feed.ts +30 -3
- package/src/feeds-client/feeds-client.ts +127 -6
- package/src/gen/feeds/FeedsApi.ts +5 -0
- package/src/gen/model-decoders/decoders.ts +10 -4
- package/src/gen/models/index.ts +75 -834
- package/src/test-utils/response-generators.ts +37 -1
- package/src/utils/throttling/index.ts +2 -0
- package/src/utils/throttling/throttle.ts +123 -0
- package/src/utils/throttling/throttled-get-batched-own-capabilities.ts +42 -0
- package/dist/index-BZL77zNq.mjs.map +0 -1
- package/dist/index-nq6SDtbt.js.map +0 -1
- package/dist/types/feed/event-handlers/aggregated-feed/handle-aggregated-feed-updated.d.ts.map +0 -1
- package/dist/types/feed/event-handlers/aggregated-feed/index.d.ts +0 -2
- package/dist/types/feed/event-handlers/aggregated-feed/index.d.ts.map +0 -1
- package/src/feed/event-handlers/activity/activity-marked-utils.test.ts +0 -208
- package/src/feed/event-handlers/activity/activity-reaction-utils.test.ts +0 -371
- package/src/feed/event-handlers/activity/activity-utils.test.ts +0 -252
- package/src/feed/event-handlers/activity/handle-activity-added.test.ts +0 -86
- package/src/feed/event-handlers/activity/handle-activity-deleted.test.ts +0 -117
- package/src/feed/event-handlers/activity/handle-activity-pinned.test.ts +0 -60
- package/src/feed/event-handlers/activity/handle-activity-reaction-added.test.ts +0 -257
- package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +0 -317
- package/src/feed/event-handlers/activity/handle-activity-reaction-updated.test.ts +0 -282
- package/src/feed/event-handlers/activity/handle-activity-unpinned.test.ts +0 -95
- package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +0 -245
- package/src/feed/event-handlers/aggregated-feed/handle-aggregated-feed-updated.test.ts +0 -644
- package/src/feed/event-handlers/aggregated-feed/index.ts +0 -1
- package/src/feed/event-handlers/bookmark/bookmark-utils.test.ts +0 -521
- package/src/feed/event-handlers/bookmark/handle-bookmark-added.test.ts +0 -178
- package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.test.ts +0 -188
- package/src/feed/event-handlers/bookmark/handle-bookmark-updated.test.ts +0 -196
- package/src/feed/event-handlers/comment/handle-comment-added.test.ts +0 -271
- package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +0 -255
- package/src/feed/event-handlers/comment/handle-comment-reaction-added.test.ts +0 -329
- package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.test.ts +0 -343
- package/src/feed/event-handlers/comment/handle-comment-reaction-updated.test.ts +0 -350
- package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +0 -267
- package/src/feed/event-handlers/comment/utils/update-comment-count.test.ts +0 -322
- package/src/feed/event-handlers/feed-member/handle-feed-member-added.test.ts +0 -75
- package/src/feed/event-handlers/feed-member/handle-feed-member-removed.test.ts +0 -82
- package/src/feed/event-handlers/feed-member/handle-feed-member-updated.test.ts +0 -84
- package/src/feed/event-handlers/follow/follow-state-update-queue.test.ts +0 -219
- package/src/feed/event-handlers/follow/handle-follow-created.test.ts +0 -250
- package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +0 -268
- package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +0 -131
- package/src/feed/feed.test.ts +0 -90
- package/src/feeds-client/event-handlers/user/handle-user-updated.test.ts +0 -53
- package/src/utils/event-triggered-by-connected-user.test.ts +0 -73
- package/src/utils/state-update-queue.test.ts +0 -129
- package/src/utils/unique-array-merge.test.ts +0 -179
|
@@ -1,84 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type Feed, type FeedState, FeedOwnCapability } from '@self';
|
|
1
|
+
import type { FeedOwnCapability, Feed, FeedsClientState } from '@self';
|
|
3
2
|
import { useStateStore } from '@stream-io/state-store/react-bindings';
|
|
4
3
|
import { useFeedContext } from '../../contexts/StreamFeedContext';
|
|
4
|
+
import { useFeedsClient } from '../../contexts/StreamFeedsContext';
|
|
5
|
+
import { useCallback } from 'react';
|
|
5
6
|
|
|
6
7
|
const stableEmptyArray: readonly FeedOwnCapability[] = [];
|
|
7
8
|
|
|
8
|
-
const selector = (currentState: FeedState) => ({
|
|
9
|
-
oc: currentState.own_capabilities ?? stableEmptyArray,
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
type KebabToSnakeCase<S extends string> = S extends `${infer T}-${infer U}`
|
|
13
|
-
? `${T}_${KebabToSnakeCase<U>}`
|
|
14
|
-
: S;
|
|
15
|
-
|
|
16
9
|
export const useOwnCapabilities = (feedFromProps?: Feed) => {
|
|
10
|
+
const client = useFeedsClient();
|
|
17
11
|
const feedFromContext = useFeedContext();
|
|
18
12
|
const feed = feedFromProps ?? feedFromContext;
|
|
13
|
+
const fid = feed?.feed;
|
|
14
|
+
|
|
15
|
+
const selector = useCallback((currentState: FeedsClientState) => {
|
|
16
|
+
if (!fid) {
|
|
17
|
+
return { feedOwnCapabilities: stableEmptyArray };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
feedOwnCapabilities:
|
|
22
|
+
currentState.own_capabilities_by_fid[fid] ?? stableEmptyArray,
|
|
23
|
+
};
|
|
24
|
+
}, [fid]);
|
|
25
|
+
|
|
26
|
+
const { feedOwnCapabilities = stableEmptyArray } =
|
|
27
|
+
useStateStore(client?.state, selector) ?? {};
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
// console.log('GETTING CAPA: ', feed?.feed, feedOwnCapabilities);
|
|
21
30
|
|
|
22
|
-
return
|
|
23
|
-
() => {
|
|
24
|
-
const capabilitiesSet = new Set(oc);
|
|
25
|
-
return ({
|
|
26
|
-
can_add_activity: capabilitiesSet.has(FeedOwnCapability.ADD_ACTIVITY),
|
|
27
|
-
can_add_activity_bookmark:
|
|
28
|
-
capabilitiesSet.has(FeedOwnCapability.ADD_ACTIVITY_BOOKMARK),
|
|
29
|
-
can_add_activity_reaction:
|
|
30
|
-
capabilitiesSet.has(FeedOwnCapability.ADD_ACTIVITY_REACTION),
|
|
31
|
-
can_add_comment: capabilitiesSet.has(FeedOwnCapability.ADD_COMMENT),
|
|
32
|
-
can_add_comment_reaction:
|
|
33
|
-
capabilitiesSet.has(FeedOwnCapability.ADD_COMMENT_REACTION),
|
|
34
|
-
can_create_feed: capabilitiesSet.has(FeedOwnCapability.CREATE_FEED),
|
|
35
|
-
can_delete_any_activity:
|
|
36
|
-
capabilitiesSet.has(FeedOwnCapability.DELETE_ANY_ACTIVITY),
|
|
37
|
-
can_delete_any_comment:
|
|
38
|
-
capabilitiesSet.has(FeedOwnCapability.DELETE_ANY_COMMENT),
|
|
39
|
-
can_delete_feed: capabilitiesSet.has(FeedOwnCapability.DELETE_FEED),
|
|
40
|
-
can_delete_own_activity:
|
|
41
|
-
capabilitiesSet.has(FeedOwnCapability.DELETE_OWN_ACTIVITY),
|
|
42
|
-
can_delete_own_activity_bookmark:
|
|
43
|
-
capabilitiesSet.has(FeedOwnCapability.DELETE_OWN_ACTIVITY_BOOKMARK),
|
|
44
|
-
can_delete_own_activity_reaction:
|
|
45
|
-
capabilitiesSet.has(FeedOwnCapability.DELETE_OWN_ACTIVITY_REACTION),
|
|
46
|
-
can_delete_own_comment:
|
|
47
|
-
capabilitiesSet.has(FeedOwnCapability.DELETE_OWN_COMMENT),
|
|
48
|
-
can_delete_own_comment_reaction:
|
|
49
|
-
capabilitiesSet.has(FeedOwnCapability.DELETE_OWN_COMMENT_REACTION),
|
|
50
|
-
can_follow: capabilitiesSet.has(FeedOwnCapability.FOLLOW),
|
|
51
|
-
can_pin_activity: capabilitiesSet.has(FeedOwnCapability.PIN_ACTIVITY),
|
|
52
|
-
can_query_feed_members:
|
|
53
|
-
capabilitiesSet.has(FeedOwnCapability.QUERY_FEED_MEMBERS),
|
|
54
|
-
can_query_follows:
|
|
55
|
-
capabilitiesSet.has(FeedOwnCapability.QUERY_FOLLOWS),
|
|
56
|
-
can_read_activities:
|
|
57
|
-
capabilitiesSet.has(FeedOwnCapability.READ_ACTIVITIES),
|
|
58
|
-
can_read_feed: capabilitiesSet.has(FeedOwnCapability.READ_FEED),
|
|
59
|
-
can_unfollow: capabilitiesSet.has(FeedOwnCapability.UNFOLLOW),
|
|
60
|
-
can_update_any_activity:
|
|
61
|
-
capabilitiesSet.has(FeedOwnCapability.UPDATE_ANY_ACTIVITY),
|
|
62
|
-
can_update_any_comment:
|
|
63
|
-
capabilitiesSet.has(FeedOwnCapability.UPDATE_ANY_COMMENT),
|
|
64
|
-
can_update_feed: capabilitiesSet.has(FeedOwnCapability.UPDATE_FEED),
|
|
65
|
-
can_update_feed_followers:
|
|
66
|
-
capabilitiesSet.has(FeedOwnCapability.UPDATE_FEED_FOLLOWERS),
|
|
67
|
-
can_update_feed_members:
|
|
68
|
-
capabilitiesSet.has(FeedOwnCapability.UPDATE_FEED_MEMBERS),
|
|
69
|
-
can_update_own_activity:
|
|
70
|
-
capabilitiesSet.has(FeedOwnCapability.UPDATE_OWN_ACTIVITY),
|
|
71
|
-
can_update_own_activity_bookmark:
|
|
72
|
-
capabilitiesSet.has(FeedOwnCapability.UPDATE_OWN_ACTIVITY_BOOKMARK),
|
|
73
|
-
can_update_own_comment:
|
|
74
|
-
capabilitiesSet.has(FeedOwnCapability.UPDATE_OWN_COMMENT),
|
|
75
|
-
}) satisfies Record<
|
|
76
|
-
`can_${KebabToSnakeCase<
|
|
77
|
-
(typeof FeedOwnCapability)[keyof typeof FeedOwnCapability]
|
|
78
|
-
>}`,
|
|
79
|
-
boolean
|
|
80
|
-
>;
|
|
81
|
-
},
|
|
82
|
-
[oc],
|
|
83
|
-
);
|
|
31
|
+
return feedOwnCapabilities;
|
|
84
32
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { OwnUser } from '
|
|
1
|
+
import type { OwnUser } from '@self';
|
|
2
|
+
import type { StreamApiError } from '@self';
|
|
2
3
|
|
|
3
4
|
export interface ConnectionChangedEvent {
|
|
4
5
|
type: 'connection.changed';
|
|
@@ -39,9 +40,10 @@ export interface ConnectedEvent {
|
|
|
39
40
|
|
|
40
41
|
export enum UnhandledErrorType {
|
|
41
42
|
ReconnectionReconciliation = 'reconnection-reconciliation',
|
|
43
|
+
FetchingOwnCapabilitiesOnNewActivity = 'fetching-own-capabilities-on-new-activity',
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
export type SyncFailure = { feed: string
|
|
46
|
+
export type SyncFailure = { feed: string; reason: unknown };
|
|
45
47
|
|
|
46
48
|
export type UnhandledErrorEvent = {
|
|
47
49
|
type: 'errors.unhandled';
|
|
@@ -51,4 +53,8 @@ export type UnhandledErrorEvent = {
|
|
|
51
53
|
error_type: UnhandledErrorType.ReconnectionReconciliation;
|
|
52
54
|
failures: SyncFailure[];
|
|
53
55
|
}
|
|
56
|
+
| {
|
|
57
|
+
error_type: UnhandledErrorType.FetchingOwnCapabilitiesOnNewActivity;
|
|
58
|
+
error: StreamApiError;
|
|
59
|
+
}
|
|
54
60
|
);
|
package/src/common/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Feed } from '../../feed';
|
|
2
2
|
import type { ActivityResponse } from '../../../gen/models';
|
|
3
|
-
import type { EventPayload
|
|
3
|
+
import type { EventPayload } from '../../../types-internal';
|
|
4
4
|
|
|
5
5
|
export function addActivitiesToState(
|
|
6
6
|
this: Feed,
|
|
@@ -8,20 +8,18 @@ export function addActivitiesToState(
|
|
|
8
8
|
activities: ActivityResponse[] | undefined,
|
|
9
9
|
position: 'start' | 'end',
|
|
10
10
|
) {
|
|
11
|
-
let result: UpdateStateResult<{ activities: ActivityResponse[] }>;
|
|
12
11
|
if (activities === undefined) {
|
|
13
|
-
|
|
14
|
-
result = {
|
|
15
|
-
changed: true,
|
|
16
|
-
activities,
|
|
17
|
-
};
|
|
18
|
-
} else {
|
|
19
|
-
result = {
|
|
12
|
+
return {
|
|
20
13
|
changed: false,
|
|
21
|
-
activities,
|
|
14
|
+
activities: [],
|
|
22
15
|
};
|
|
23
16
|
}
|
|
24
17
|
|
|
18
|
+
let result = {
|
|
19
|
+
changed: false,
|
|
20
|
+
activities,
|
|
21
|
+
};
|
|
22
|
+
|
|
25
23
|
const newActivitiesDeduplicated: ActivityResponse[] = [];
|
|
26
24
|
newActivities.forEach((newActivityResponse) => {
|
|
27
25
|
if (!this.hasActivity(newActivityResponse.id)) {
|
|
@@ -51,10 +49,18 @@ export function handleActivityAdded(
|
|
|
51
49
|
const result = addActivitiesToState.bind(this)(
|
|
52
50
|
[event.activity],
|
|
53
51
|
currentActivities,
|
|
54
|
-
|
|
52
|
+
this.currentState.addNewActivitiesTo,
|
|
55
53
|
);
|
|
56
54
|
if (result.changed) {
|
|
57
|
-
|
|
55
|
+
const activity = event.activity;
|
|
56
|
+
this.client.hydratePollCache([activity]);
|
|
57
|
+
|
|
58
|
+
const currentFeed = activity.current_feed;
|
|
59
|
+
|
|
60
|
+
if (currentFeed) {
|
|
61
|
+
this.client.hydrateCapabilitiesCache([currentFeed]);
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
this.state.partialNext({ activities: result.activities });
|
|
59
65
|
}
|
|
60
66
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
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, PartializeAllBut } from '../../../types-internal';
|
|
4
7
|
import {
|
|
5
8
|
getStateUpdateQueueId,
|
|
@@ -7,25 +10,14 @@ import {
|
|
|
7
10
|
updateEntityInArray,
|
|
8
11
|
} from '../../../utils';
|
|
9
12
|
import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
|
|
13
|
+
import { updateActivity } from '../activity-updater';
|
|
10
14
|
|
|
11
15
|
export type ActivityUpdatedPayload = PartializeAllBut<
|
|
12
16
|
EventPayload<'feeds.activity.updated'>,
|
|
13
17
|
'activity'
|
|
14
18
|
>;
|
|
15
19
|
|
|
16
|
-
const sharedUpdateActivity =
|
|
17
|
-
currentActivity,
|
|
18
|
-
event,
|
|
19
|
-
}: {
|
|
20
|
-
currentActivity: ActivityResponse;
|
|
21
|
-
event: ActivityUpdatedPayload;
|
|
22
|
-
}) => {
|
|
23
|
-
return {
|
|
24
|
-
...event.activity,
|
|
25
|
-
own_reactions: currentActivity.own_reactions,
|
|
26
|
-
own_bookmarks: currentActivity.own_bookmarks,
|
|
27
|
-
};
|
|
28
|
-
};
|
|
20
|
+
const sharedUpdateActivity = updateActivity;
|
|
29
21
|
|
|
30
22
|
export const updateActivityInState = (
|
|
31
23
|
event: ActivityUpdatedPayload,
|
|
@@ -37,7 +29,7 @@ export const updateActivityInState = (
|
|
|
37
29
|
updater: (matchedActivity) =>
|
|
38
30
|
sharedUpdateActivity({
|
|
39
31
|
currentActivity: matchedActivity,
|
|
40
|
-
event,
|
|
32
|
+
newActivtiy: event.activity,
|
|
41
33
|
}),
|
|
42
34
|
});
|
|
43
35
|
|
|
@@ -52,7 +44,7 @@ export const updatePinnedActivityInState = (
|
|
|
52
44
|
updater: (matchedPinnedActivity) => {
|
|
53
45
|
const newActivity = sharedUpdateActivity({
|
|
54
46
|
currentActivity: matchedPinnedActivity.activity,
|
|
55
|
-
event,
|
|
47
|
+
newActivtiy: event.activity,
|
|
56
48
|
});
|
|
57
49
|
|
|
58
50
|
if (newActivity === matchedPinnedActivity.activity) {
|
|
@@ -100,6 +92,10 @@ export function handleActivityUpdated(
|
|
|
100
92
|
if (result1?.changed || result2.changed) {
|
|
101
93
|
this.client.hydratePollCache([payload.activity]);
|
|
102
94
|
|
|
95
|
+
if (payload.activity.current_feed) {
|
|
96
|
+
this.client.hydrateCapabilitiesCache([payload.activity.current_feed]);
|
|
97
|
+
}
|
|
98
|
+
|
|
103
99
|
this.state.partialNext({
|
|
104
100
|
activities: result1?.changed ? result1.entities : currentActivities,
|
|
105
101
|
pinned_activities: result2.entities,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ActivityResponse } from '../../gen/models';
|
|
2
|
+
|
|
3
|
+
export const updateActivity = ({
|
|
4
|
+
currentActivity,
|
|
5
|
+
newActivtiy,
|
|
6
|
+
}: {
|
|
7
|
+
currentActivity: ActivityResponse;
|
|
8
|
+
newActivtiy: ActivityResponse;
|
|
9
|
+
}) => {
|
|
10
|
+
return {
|
|
11
|
+
...newActivtiy,
|
|
12
|
+
own_reactions: currentActivity.own_reactions,
|
|
13
|
+
own_bookmarks: currentActivity.own_bookmarks,
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AggregatedActivityResponse } from '../../gen/models';
|
|
2
|
+
import type { UpdateStateResult } from '../../types-internal';
|
|
3
|
+
import { replaceUniqueArrayMerge, uniqueArrayMerge } from '../../utils';
|
|
4
|
+
import { updateActivity } from './activity-updater';
|
|
5
|
+
|
|
6
|
+
export const addAggregatedActivitiesToState = (
|
|
7
|
+
newAggregatedActivities: AggregatedActivityResponse[],
|
|
8
|
+
aggregatedActivities: AggregatedActivityResponse[] | undefined,
|
|
9
|
+
position: 'start' | 'end' | 'replace',
|
|
10
|
+
) => {
|
|
11
|
+
let result: UpdateStateResult<{
|
|
12
|
+
aggregated_activities: AggregatedActivityResponse[];
|
|
13
|
+
}>;
|
|
14
|
+
if (newAggregatedActivities.length === 0) {
|
|
15
|
+
result = {
|
|
16
|
+
changed: false,
|
|
17
|
+
aggregated_activities: aggregatedActivities ?? [],
|
|
18
|
+
};
|
|
19
|
+
} else {
|
|
20
|
+
result = {
|
|
21
|
+
changed: true,
|
|
22
|
+
aggregated_activities: [],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Merge update activities in the group
|
|
26
|
+
newAggregatedActivities.forEach((newAggregatedActivity) => {
|
|
27
|
+
const existingAggregatedActivity = aggregatedActivities?.find(
|
|
28
|
+
(a) => a.group === newAggregatedActivity.group,
|
|
29
|
+
);
|
|
30
|
+
if (existingAggregatedActivity) {
|
|
31
|
+
for (let i = 0; i < newAggregatedActivity.activities.length; i++) {
|
|
32
|
+
const activity = newAggregatedActivity.activities[i];
|
|
33
|
+
const existingActivity = existingAggregatedActivity.activities.find(
|
|
34
|
+
(a) => a.id === activity.id,
|
|
35
|
+
);
|
|
36
|
+
if (existingActivity) {
|
|
37
|
+
newAggregatedActivity.activities[i] = updateActivity({
|
|
38
|
+
currentActivity: existingActivity,
|
|
39
|
+
newActivtiy: activity,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
switch (position) {
|
|
47
|
+
case 'start':
|
|
48
|
+
result.aggregated_activities = uniqueArrayMerge(
|
|
49
|
+
newAggregatedActivities,
|
|
50
|
+
aggregatedActivities ?? [],
|
|
51
|
+
(a) => a.group,
|
|
52
|
+
);
|
|
53
|
+
break;
|
|
54
|
+
case 'end':
|
|
55
|
+
result.aggregated_activities = uniqueArrayMerge(
|
|
56
|
+
aggregatedActivities ?? [],
|
|
57
|
+
newAggregatedActivities,
|
|
58
|
+
(a) => a.group,
|
|
59
|
+
);
|
|
60
|
+
break;
|
|
61
|
+
case 'replace':
|
|
62
|
+
result.aggregated_activities = replaceUniqueArrayMerge(
|
|
63
|
+
aggregatedActivities ?? [],
|
|
64
|
+
newAggregatedActivities,
|
|
65
|
+
(a) => a.group,
|
|
66
|
+
);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
@@ -4,5 +4,7 @@ export * from './feed-member';
|
|
|
4
4
|
export * from './bookmark';
|
|
5
5
|
export * from './activity';
|
|
6
6
|
export * from './feed';
|
|
7
|
-
export * from './
|
|
7
|
+
export * from './notification-feed';
|
|
8
|
+
export * from './story-feeds';
|
|
8
9
|
export * from './watch';
|
|
10
|
+
export * from './add-aggregated-activities-to-state';
|
|
@@ -3,57 +3,9 @@ import type {
|
|
|
3
3
|
AggregatedActivityResponse,
|
|
4
4
|
NotificationFeedUpdatedEvent,
|
|
5
5
|
NotificationStatusResponse,
|
|
6
|
-
StoriesFeedUpdatedEvent,
|
|
7
6
|
} from '../../../gen/models';
|
|
8
7
|
import type { EventPayload, UpdateStateResult } from '../../../types-internal';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
export const addAggregatedActivitiesToState = (
|
|
12
|
-
newAggregatedActivities: AggregatedActivityResponse[],
|
|
13
|
-
aggregatedActivities: AggregatedActivityResponse[] | undefined,
|
|
14
|
-
position: 'start' | 'end' | 'replace',
|
|
15
|
-
) => {
|
|
16
|
-
let result: UpdateStateResult<{
|
|
17
|
-
aggregated_activities: AggregatedActivityResponse[];
|
|
18
|
-
}>;
|
|
19
|
-
if (newAggregatedActivities.length === 0) {
|
|
20
|
-
result = {
|
|
21
|
-
changed: false,
|
|
22
|
-
aggregated_activities: [],
|
|
23
|
-
};
|
|
24
|
-
} else {
|
|
25
|
-
result = {
|
|
26
|
-
changed: true,
|
|
27
|
-
aggregated_activities: [],
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
switch (position) {
|
|
32
|
-
case 'start':
|
|
33
|
-
result.aggregated_activities = uniqueArrayMerge(
|
|
34
|
-
newAggregatedActivities,
|
|
35
|
-
aggregatedActivities ?? [],
|
|
36
|
-
(a) => a.group,
|
|
37
|
-
);
|
|
38
|
-
break;
|
|
39
|
-
case 'end':
|
|
40
|
-
result.aggregated_activities = uniqueArrayMerge(
|
|
41
|
-
aggregatedActivities ?? [],
|
|
42
|
-
newAggregatedActivities,
|
|
43
|
-
(a) => a.group,
|
|
44
|
-
);
|
|
45
|
-
break;
|
|
46
|
-
case 'replace':
|
|
47
|
-
result.aggregated_activities = replaceUniqueArrayMerge(
|
|
48
|
-
aggregatedActivities ?? [],
|
|
49
|
-
newAggregatedActivities,
|
|
50
|
-
(a) => a.group,
|
|
51
|
-
);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return result;
|
|
56
|
-
};
|
|
8
|
+
import { addAggregatedActivitiesToState } from '../add-aggregated-activities-to-state';
|
|
57
9
|
|
|
58
10
|
export const updateNotificationStatus = (
|
|
59
11
|
newNotificationStatus?: NotificationStatusResponse,
|
|
@@ -94,7 +46,7 @@ export const updateNotificationFeedFromEvent = (
|
|
|
94
46
|
aggregated_activities?: AggregatedActivityResponse[];
|
|
95
47
|
} = {};
|
|
96
48
|
|
|
97
|
-
if (event.notification_status
|
|
49
|
+
if (event.notification_status) {
|
|
98
50
|
const notificationStatusResult = updateNotificationStatus(
|
|
99
51
|
event.notification_status,
|
|
100
52
|
currentNotificationStatus,
|
|
@@ -147,47 +99,3 @@ export function handleNotificationFeedUpdated(
|
|
|
147
99
|
});
|
|
148
100
|
}
|
|
149
101
|
}
|
|
150
|
-
|
|
151
|
-
export function updateStoriesFeedFromEvent(
|
|
152
|
-
aggregatedActivities: AggregatedActivityResponse[] | undefined,
|
|
153
|
-
event: StoriesFeedUpdatedEvent,
|
|
154
|
-
): UpdateStateResult<{
|
|
155
|
-
data?: {
|
|
156
|
-
aggregated_activities?: AggregatedActivityResponse[];
|
|
157
|
-
};
|
|
158
|
-
}> {
|
|
159
|
-
if (!aggregatedActivities) {
|
|
160
|
-
return {
|
|
161
|
-
changed: false,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (event.aggregated_activities) {
|
|
166
|
-
const result = addAggregatedActivitiesToState(
|
|
167
|
-
event.aggregated_activities,
|
|
168
|
-
aggregatedActivities,
|
|
169
|
-
'replace',
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
return result;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
changed: false,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export function handleStoriesFeedUpdated(
|
|
181
|
-
this: Feed,
|
|
182
|
-
event: EventPayload<'feeds.stories_feed.updated'>,
|
|
183
|
-
) {
|
|
184
|
-
const result = updateStoriesFeedFromEvent(
|
|
185
|
-
this.currentState.aggregated_activities,
|
|
186
|
-
event,
|
|
187
|
-
);
|
|
188
|
-
if (result.changed) {
|
|
189
|
-
this.state.partialNext({
|
|
190
|
-
aggregated_activities: result.data?.aggregated_activities,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './handle-notification-feed-updated';
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActivityResponse,
|
|
3
|
+
AggregatedActivityResponse,
|
|
4
|
+
StoriesFeedUpdatedEvent,
|
|
5
|
+
} from '../../../gen/models';
|
|
6
|
+
import type { UpdateStateResult, EventPayload } from '../../../types-internal';
|
|
7
|
+
import type { Feed } from '../../feed';
|
|
8
|
+
import { updateActivity } from '../activity-updater';
|
|
9
|
+
import { addAggregatedActivitiesToState } from '../add-aggregated-activities-to-state';
|
|
10
|
+
|
|
11
|
+
export const updateActivities = (
|
|
12
|
+
activitiesToUpsert: ActivityResponse[],
|
|
13
|
+
currentActivities: ActivityResponse[] | undefined,
|
|
14
|
+
) => {
|
|
15
|
+
if (
|
|
16
|
+
!activitiesToUpsert ||
|
|
17
|
+
activitiesToUpsert.length === 0 ||
|
|
18
|
+
!currentActivities
|
|
19
|
+
) {
|
|
20
|
+
return {
|
|
21
|
+
changed: false,
|
|
22
|
+
activities: currentActivities ?? [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result: ActivityResponse[] = [];
|
|
27
|
+
for (let i = 0; i < currentActivities.length; i++) {
|
|
28
|
+
const activity = currentActivities[i];
|
|
29
|
+
const updatedActivity = activitiesToUpsert.find(
|
|
30
|
+
(a) => a.id === activity.id,
|
|
31
|
+
);
|
|
32
|
+
if (updatedActivity) {
|
|
33
|
+
result.push(
|
|
34
|
+
updateActivity({
|
|
35
|
+
currentActivity: activity,
|
|
36
|
+
newActivtiy: updatedActivity,
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
} else {
|
|
40
|
+
result.push(activity);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
changed: true,
|
|
46
|
+
activities: result,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export function updateStoriesFeedFromEvent(
|
|
51
|
+
aggregatedActivities: AggregatedActivityResponse[] | undefined,
|
|
52
|
+
activities: ActivityResponse[] | undefined,
|
|
53
|
+
event: StoriesFeedUpdatedEvent,
|
|
54
|
+
): UpdateStateResult<{
|
|
55
|
+
data?: {
|
|
56
|
+
aggregated_activities?: AggregatedActivityResponse[];
|
|
57
|
+
activities?: ActivityResponse[];
|
|
58
|
+
};
|
|
59
|
+
}> {
|
|
60
|
+
if (
|
|
61
|
+
(!aggregatedActivities &&
|
|
62
|
+
event.aggregated_activities &&
|
|
63
|
+
event.aggregated_activities?.length > 0) ||
|
|
64
|
+
(!activities && event.activities && event.activities?.length > 0)
|
|
65
|
+
) {
|
|
66
|
+
return {
|
|
67
|
+
changed: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const result = {
|
|
72
|
+
changed: true,
|
|
73
|
+
data: {
|
|
74
|
+
aggregated_activities: aggregatedActivities,
|
|
75
|
+
activities: activities,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
if (event.aggregated_activities) {
|
|
79
|
+
const aggregatedActivitiesResult = addAggregatedActivitiesToState(
|
|
80
|
+
event.aggregated_activities,
|
|
81
|
+
aggregatedActivities,
|
|
82
|
+
'replace',
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (aggregatedActivitiesResult.changed) {
|
|
86
|
+
result.data.aggregated_activities =
|
|
87
|
+
aggregatedActivitiesResult.aggregated_activities;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (event.activities) {
|
|
92
|
+
const activitiesResult = updateActivities(event.activities, activities);
|
|
93
|
+
if (activitiesResult.changed) {
|
|
94
|
+
result.data.activities = activitiesResult.activities;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (event.aggregated_activities || event.activities) {
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
changed: false,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function handleStoriesFeedUpdated(
|
|
108
|
+
this: Feed,
|
|
109
|
+
event: EventPayload<'feeds.stories_feed.updated'>,
|
|
110
|
+
) {
|
|
111
|
+
const result = updateStoriesFeedFromEvent(
|
|
112
|
+
this.currentState.aggregated_activities,
|
|
113
|
+
this.currentState.activities,
|
|
114
|
+
event,
|
|
115
|
+
);
|
|
116
|
+
if (result.changed) {
|
|
117
|
+
this.state.partialNext({
|
|
118
|
+
aggregated_activities: result.data?.aggregated_activities,
|
|
119
|
+
activities: result.data?.activities,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './handle-story-feeds-updated';
|
package/src/feed/feed.ts
CHANGED
|
@@ -62,7 +62,7 @@ import { checkHasAnotherPage, Constants, uniqueArrayMerge } from '../utils';
|
|
|
62
62
|
|
|
63
63
|
export type FeedState = Omit<
|
|
64
64
|
Partial<GetOrCreateFeedResponse & FeedResponse>,
|
|
65
|
-
'feed' | 'duration'
|
|
65
|
+
'feed' | 'own_capabilities' | 'duration'
|
|
66
66
|
> & {
|
|
67
67
|
/**
|
|
68
68
|
* True when loading state using `getOrCreate`
|
|
@@ -131,6 +131,12 @@ export type FeedState = Omit<
|
|
|
131
131
|
* `true` if the feed is receiving real-time updates via WebSocket
|
|
132
132
|
*/
|
|
133
133
|
watch: boolean;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* When a new activity is received from a WebSocket event by default it's added to the start of the list. You can change this to `end` to add it to the end of the list.
|
|
137
|
+
* Useful for story feeds.
|
|
138
|
+
*/
|
|
139
|
+
addNewActivitiesTo: 'start' | 'end';
|
|
134
140
|
};
|
|
135
141
|
|
|
136
142
|
type EventHandlerByEventType = {
|
|
@@ -212,6 +218,7 @@ export class Feed extends FeedApi {
|
|
|
212
218
|
id: string,
|
|
213
219
|
data?: FeedResponse,
|
|
214
220
|
watch = false,
|
|
221
|
+
addNewActivitiesTo: 'start' | 'end' = 'start',
|
|
215
222
|
) {
|
|
216
223
|
super(client, groupId, id);
|
|
217
224
|
this.state = new StateStore<FeedState>({
|
|
@@ -223,6 +230,7 @@ export class Feed extends FeedApi {
|
|
|
223
230
|
is_loading_activities: false,
|
|
224
231
|
comments_by_entity_id: {},
|
|
225
232
|
watch,
|
|
233
|
+
addNewActivitiesTo,
|
|
226
234
|
});
|
|
227
235
|
this.client = client;
|
|
228
236
|
|
|
@@ -250,6 +258,10 @@ export class Feed extends FeedApi {
|
|
|
250
258
|
return this.state.getLatestValue();
|
|
251
259
|
}
|
|
252
260
|
|
|
261
|
+
set addNewActivitiesTo(value: 'start' | 'end') {
|
|
262
|
+
this.state.partialNext({ addNewActivitiesTo: value });
|
|
263
|
+
}
|
|
264
|
+
|
|
253
265
|
hasActivity(activityId: string) {
|
|
254
266
|
return this.indexedActivityIds.has(activityId);
|
|
255
267
|
}
|
|
@@ -276,6 +288,16 @@ export class Feed extends FeedApi {
|
|
|
276
288
|
|
|
277
289
|
try {
|
|
278
290
|
const response = await super.getOrCreate(request);
|
|
291
|
+
|
|
292
|
+
const currentActivityFeeds = [];
|
|
293
|
+
for (const activity of response.activities) {
|
|
294
|
+
if (activity.current_feed) {
|
|
295
|
+
currentActivityFeeds.push(activity.current_feed);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this.client.hydrateCapabilitiesCache([response.feed, ...currentActivityFeeds]);
|
|
300
|
+
|
|
279
301
|
if (request?.next) {
|
|
280
302
|
const { activities: currentActivities = [] } = this.currentState;
|
|
281
303
|
|
|
@@ -837,11 +859,16 @@ export class Feed extends FeedApi {
|
|
|
837
859
|
});
|
|
838
860
|
}
|
|
839
861
|
|
|
840
|
-
addActivity(request: Omit<ActivityRequest, 'feeds'>) {
|
|
841
|
-
|
|
862
|
+
async addActivity(request: Omit<ActivityRequest, 'feeds'>) {
|
|
863
|
+
const response = await this.client.addActivity({
|
|
842
864
|
...request,
|
|
843
865
|
feeds: [this.feed],
|
|
844
866
|
});
|
|
867
|
+
const currentFeed = response.activity.current_feed;
|
|
868
|
+
if (currentFeed) {
|
|
869
|
+
this.client.hydrateCapabilitiesCache([currentFeed]);
|
|
870
|
+
}
|
|
871
|
+
return response;
|
|
845
872
|
}
|
|
846
873
|
|
|
847
874
|
on = this.eventDispatcher.on;
|