@stream-io/feeds-client 0.3.32 → 0.3.34
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 +18 -0
- package/README.md +3 -2
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react-bindings.js +40 -36
- package/dist/cjs/react-bindings.js.map +1 -1
- package/dist/es/index.mjs +4 -3
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/react-bindings.mjs +40 -36
- package/dist/es/react-bindings.mjs.map +1 -1
- package/dist/{feeds-client-BNhvggk2.mjs → feeds-client-C-2_fdH1.mjs} +229 -209
- package/dist/feeds-client-C-2_fdH1.mjs.map +1 -0
- package/dist/{feeds-client-B9rwEWH3.js → feeds-client-Xj6kDjVH.js} +229 -209
- package/dist/feeds-client-Xj6kDjVH.js.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/types/activity-with-state-updates/activity-with-state-updates.d.ts +4 -2
- package/dist/types/activity-with-state-updates/activity-with-state-updates.d.ts.map +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/index.d.ts +1 -0
- package/dist/types/bindings/react/hooks/feed-state-hooks/index.d.ts.map +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts.map +1 -1
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnFollowings.d.ts +8 -0
- package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnFollowings.d.ts.map +1 -0
- package/dist/types/bindings/react/hooks/useCreateFeedsClient.d.ts.map +1 -1
- package/dist/types/common/types.d.ts +1 -1
- package/dist/types/common/types.d.ts.map +1 -1
- 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/feed.d.ts +2 -1
- package/dist/types/feed/feed.d.ts.map +1 -1
- package/dist/types/feeds-client/active-activity.d.ts +2 -1
- package/dist/types/feeds-client/active-activity.d.ts.map +1 -1
- package/dist/types/feeds-client/feeds-client.d.ts +14 -12
- package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
- package/dist/types/utils/check-own-fields-equality.d.ts +2 -0
- package/dist/types/utils/check-own-fields-equality.d.ts.map +1 -1
- package/dist/types/utils/throttling/index.d.ts +1 -1
- package/dist/types/utils/throttling/index.d.ts.map +1 -1
- package/dist/types/utils/throttling/throttled-get-batched-own-fields.d.ts +14 -0
- package/dist/types/utils/throttling/throttled-get-batched-own-fields.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/activity-with-state-updates/activity-with-state-updates.ts +17 -3
- package/src/bindings/react/hooks/feed-state-hooks/index.ts +1 -0
- package/src/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.ts +14 -23
- package/src/bindings/react/hooks/feed-state-hooks/useOwnFollowings.ts +18 -0
- package/src/bindings/react/hooks/useCreateFeedsClient.ts +1 -3
- package/src/common/types.ts +1 -1
- package/src/feed/event-handlers/activity/handle-activity-added.ts +0 -6
- package/src/feed/event-handlers/activity/handle-activity-updated.ts +0 -4
- package/src/feed/feed.ts +53 -39
- package/src/feeds-client/active-activity.ts +8 -4
- package/src/feeds-client/feeds-client.ts +110 -114
- package/src/utils/check-own-fields-equality.ts +37 -10
- package/src/utils/throttling/index.ts +1 -1
- package/src/utils/throttling/{throttled-get-batched-own-capabilities.ts → throttled-get-batched-own-fields.ts} +10 -10
- package/dist/feeds-client-B9rwEWH3.js.map +0 -1
- package/dist/feeds-client-BNhvggk2.mjs.map +0 -1
- package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts +0 -14
- package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts.map +0 -1
package/src/feed/feed.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
FollowRequest,
|
|
15
15
|
QueryCommentsRequest,
|
|
16
16
|
ActivityAddedEvent,
|
|
17
|
+
EnrichmentOptions,
|
|
17
18
|
} from '../gen/models';
|
|
18
19
|
import type { StreamResponse } from '../gen-imports';
|
|
19
20
|
import { StateStore } from '@stream-io/state-store';
|
|
@@ -63,16 +64,16 @@ import {
|
|
|
63
64
|
checkHasAnotherPage,
|
|
64
65
|
Constants,
|
|
65
66
|
feedsLoggerSystem,
|
|
66
|
-
ownFeedFields,
|
|
67
67
|
uniqueArrayMerge,
|
|
68
68
|
} from '../utils';
|
|
69
69
|
import { handleActivityFeedback } from './event-handlers/activity/handle-activity-feedback';
|
|
70
70
|
import { deepEqual } from '../utils/deep-equal';
|
|
71
71
|
import { getOrCreateActiveFeed } from '../feeds-client/get-or-create-active-feed';
|
|
72
|
+
import { queueBatchedOwnFields } from '../utils/throttling';
|
|
72
73
|
|
|
73
74
|
export type FeedState = Omit<
|
|
74
75
|
Partial<GetOrCreateFeedResponse & FeedResponse>,
|
|
75
|
-
'feed' | '
|
|
76
|
+
'feed' | 'duration'
|
|
76
77
|
> & {
|
|
77
78
|
/**
|
|
78
79
|
* True when loading state using `getOrCreate`
|
|
@@ -333,11 +334,6 @@ export class Feed extends FeedApi {
|
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
|
|
336
|
-
this.client.hydrateCapabilitiesCache([
|
|
337
|
-
response.feed,
|
|
338
|
-
...currentActivityFeeds,
|
|
339
|
-
]);
|
|
340
|
-
|
|
341
337
|
if (request?.next) {
|
|
342
338
|
const { activities: currentActivities = [] } = this.currentState;
|
|
343
339
|
|
|
@@ -919,10 +915,7 @@ export class Feed extends FeedApi {
|
|
|
919
915
|
...request,
|
|
920
916
|
feeds: [this.feed],
|
|
921
917
|
});
|
|
922
|
-
|
|
923
|
-
if (currentFeed) {
|
|
924
|
-
this.client.hydrateCapabilitiesCache([currentFeed]);
|
|
925
|
-
}
|
|
918
|
+
|
|
926
919
|
return response;
|
|
927
920
|
}
|
|
928
921
|
|
|
@@ -934,34 +927,19 @@ export class Feed extends FeedApi {
|
|
|
934
927
|
|
|
935
928
|
// no need to run noop function
|
|
936
929
|
if (eventHandler !== Feed.noop) {
|
|
937
|
-
if
|
|
930
|
+
// Backfill current_feed if activity is posted to multiple feeds
|
|
931
|
+
if (
|
|
932
|
+
'activity' in event &&
|
|
933
|
+
event.activity.feeds.length > 1 &&
|
|
934
|
+
this.hasActivity(event.activity.id)
|
|
935
|
+
) {
|
|
938
936
|
const currentActivity = this.currentState.activities?.find(
|
|
939
937
|
(a) => a.id === event.activity.id,
|
|
940
938
|
);
|
|
941
939
|
|
|
942
|
-
|
|
943
|
-
if (
|
|
944
|
-
event.activity.feeds.length > 1 &&
|
|
945
|
-
!event.activity.current_feed &&
|
|
946
|
-
currentActivity?.current_feed
|
|
947
|
-
) {
|
|
940
|
+
if (!event.activity.current_feed && currentActivity?.current_feed) {
|
|
948
941
|
event.activity.current_feed = currentActivity.current_feed;
|
|
949
942
|
}
|
|
950
|
-
|
|
951
|
-
// Backfill own_ fields if activity is posted to a single feed
|
|
952
|
-
if (
|
|
953
|
-
event.activity.feeds.length === 1 &&
|
|
954
|
-
event.activity.current_feed &&
|
|
955
|
-
currentActivity?.current_feed
|
|
956
|
-
) {
|
|
957
|
-
ownFeedFields.forEach((field) => {
|
|
958
|
-
if (field in currentActivity.current_feed!) {
|
|
959
|
-
// @ts-expect-error TODO: fix this
|
|
960
|
-
event.activity.current_feed![field] =
|
|
961
|
-
currentActivity.current_feed![field];
|
|
962
|
-
}
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
943
|
}
|
|
966
944
|
// @ts-expect-error intersection of handler arguments results to never
|
|
967
945
|
eventHandler?.(event);
|
|
@@ -994,10 +972,7 @@ export class Feed extends FeedApi {
|
|
|
994
972
|
) {
|
|
995
973
|
const enrichmentOptions =
|
|
996
974
|
this.currentState.last_get_or_create_request_config?.enrichment_options;
|
|
997
|
-
if (
|
|
998
|
-
!enrichmentOptions?.skip_activity_current_feed &&
|
|
999
|
-
!enrichmentOptions?.skip_all
|
|
1000
|
-
) {
|
|
975
|
+
if (this.shouldAddToActiveFeeds(enrichmentOptions)) {
|
|
1001
976
|
const feedsToGetOrCreate = new Map<string, FeedResponse>();
|
|
1002
977
|
activities.forEach((activity) => {
|
|
1003
978
|
if (
|
|
@@ -1010,14 +985,53 @@ export class Feed extends FeedApi {
|
|
|
1010
985
|
);
|
|
1011
986
|
}
|
|
1012
987
|
});
|
|
1013
|
-
Array.from(feedsToGetOrCreate.values())
|
|
988
|
+
const newFeeds = Array.from(feedsToGetOrCreate.values());
|
|
989
|
+
const fieldsToUpdate: Array<
|
|
990
|
+
'own_capabilities' | 'own_follows' | 'own_followings' | 'own_membership'
|
|
991
|
+
> = [];
|
|
992
|
+
if (!options.fromWebSocket) {
|
|
993
|
+
fieldsToUpdate.push('own_membership');
|
|
994
|
+
if (!enrichmentOptions?.skip_own_capabilities) {
|
|
995
|
+
fieldsToUpdate.push('own_capabilities');
|
|
996
|
+
}
|
|
997
|
+
if (!enrichmentOptions?.skip_own_follows) {
|
|
998
|
+
fieldsToUpdate.push('own_follows');
|
|
999
|
+
}
|
|
1000
|
+
if (enrichmentOptions?.enrich_own_followings) {
|
|
1001
|
+
fieldsToUpdate.push('own_followings');
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
newFeeds.forEach((feed) => {
|
|
1014
1005
|
getOrCreateActiveFeed.bind(this.client)({
|
|
1015
1006
|
group: feed.group_id,
|
|
1016
1007
|
id: feed.id,
|
|
1017
1008
|
data: feed,
|
|
1018
|
-
|
|
1009
|
+
fieldsToUpdate,
|
|
1019
1010
|
});
|
|
1020
1011
|
});
|
|
1012
|
+
if (options.fromWebSocket) {
|
|
1013
|
+
const uninitializedFeeds = newFeeds.filter((feedResponse) => {
|
|
1014
|
+
const feed = this.client.feed(feedResponse.group_id, feedResponse.id);
|
|
1015
|
+
// own_capabilities can only be undefined if we haven't fetched it yet
|
|
1016
|
+
return feed.currentState.own_capabilities === undefined;
|
|
1017
|
+
});
|
|
1018
|
+
if (uninitializedFeeds.length > 0) {
|
|
1019
|
+
queueBatchedOwnFields.bind(this.client)({
|
|
1020
|
+
feeds: uninitializedFeeds.map((feed) => feed.feed),
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
private shouldAddToActiveFeeds(enrichmentOptions?: EnrichmentOptions) {
|
|
1028
|
+
if (!enrichmentOptions) {
|
|
1029
|
+
return true;
|
|
1021
1030
|
}
|
|
1031
|
+
return (
|
|
1032
|
+
!enrichmentOptions?.skip_activity &&
|
|
1033
|
+
!enrichmentOptions?.skip_activity_current_feed &&
|
|
1034
|
+
!enrichmentOptions?.skip_all
|
|
1035
|
+
);
|
|
1022
1036
|
}
|
|
1023
1037
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ActivityWithStateUpdates } from '../activity-with-state-updates/activity-with-state-updates';
|
|
1
2
|
import { Feed } from '../feed';
|
|
2
3
|
import type { FeedsClient } from './feeds-client';
|
|
3
4
|
|
|
@@ -34,9 +35,12 @@ export function isAnyFeedWatched(this: FeedsClient, fids: string[]) {
|
|
|
34
35
|
return false;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
export function disconnectActivityFromFeed(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
export function disconnectActivityFromFeed(
|
|
39
|
+
this: FeedsClient,
|
|
40
|
+
activity: ActivityWithStateUpdates,
|
|
41
|
+
) {
|
|
42
|
+
const index = this.activeActivities.indexOf(activity);
|
|
43
|
+
if (index !== -1) {
|
|
44
|
+
this.activeActivities.splice(index, 1);
|
|
41
45
|
}
|
|
42
46
|
}
|
|
@@ -60,6 +60,7 @@ import { ModerationClient } from '../moderation-client';
|
|
|
60
60
|
import { StreamPoll } from '../common/Poll';
|
|
61
61
|
import {
|
|
62
62
|
Feed,
|
|
63
|
+
type FeedState,
|
|
63
64
|
handleActivityReactionAdded,
|
|
64
65
|
handleActivityReactionDeleted,
|
|
65
66
|
handleActivityReactionUpdated,
|
|
@@ -86,15 +87,16 @@ import { feedsLoggerSystem } from '../utils';
|
|
|
86
87
|
import { handleCommentReactionUpdated } from '../feed/event-handlers/comment/handle-comment-reaction-updated';
|
|
87
88
|
import {
|
|
88
89
|
throttle,
|
|
89
|
-
DEFAULT_BATCH_OWN_CAPABILITIES_THROTTLING_INTERVAL,
|
|
90
|
-
type GetBatchedOwnCapabilitiesThrottledCallback,
|
|
91
|
-
queueBatchedOwnCapabilities,
|
|
92
|
-
type ThrottledGetBatchedOwnCapabilities,
|
|
93
90
|
clearQueuedFeeds,
|
|
91
|
+
type ThrottledGetBatchedOwnFields,
|
|
92
|
+
type GetBatchedOwnFieldsThrottledCallback,
|
|
93
|
+
DEFAULT_BATCH_OWN_FIELDS_THROTTLING_INTERVAL,
|
|
94
94
|
} from '../utils/throttling';
|
|
95
95
|
import { ActivityWithStateUpdates } from '../activity-with-state-updates/activity-with-state-updates';
|
|
96
96
|
import { getFeed } from '../activity-with-state-updates/get-feed';
|
|
97
97
|
import {
|
|
98
|
+
isOwnCapabilitiesEqual,
|
|
99
|
+
isOwnFollowingsEqual,
|
|
98
100
|
isOwnFollowsEqual,
|
|
99
101
|
isOwnMembershipEqual,
|
|
100
102
|
} from '../utils/check-own-fields-equality';
|
|
@@ -102,7 +104,6 @@ import {
|
|
|
102
104
|
export type FeedsClientState = {
|
|
103
105
|
connected_user: ConnectedUser | undefined;
|
|
104
106
|
is_ws_connection_healthy: boolean;
|
|
105
|
-
own_capabilities_by_fid: Record<string, FeedResponse['own_capabilities']>;
|
|
106
107
|
};
|
|
107
108
|
|
|
108
109
|
type FID = string;
|
|
@@ -123,14 +124,14 @@ export class FeedsClient extends FeedsApi {
|
|
|
123
124
|
|
|
124
125
|
private readonly polls_by_id: Map<string, StreamPoll>;
|
|
125
126
|
|
|
126
|
-
protected activeActivities:
|
|
127
|
+
protected activeActivities: ActivityWithStateUpdates[] = [];
|
|
127
128
|
protected activeFeeds: Record<FID, Feed> = {};
|
|
128
129
|
|
|
129
130
|
private healthyConnectionChangedEventCount = 0;
|
|
130
131
|
|
|
131
|
-
protected
|
|
132
|
-
private
|
|
133
|
-
private
|
|
132
|
+
protected throttledGetBatchOwnFields!: ThrottledGetBatchedOwnFields;
|
|
133
|
+
private cancelGetBatchOwnFieldsTimer!: () => void;
|
|
134
|
+
private query_batch_own_fields_throttling_interval!: number;
|
|
134
135
|
|
|
135
136
|
constructor(apiKey: string, options?: FeedsClientOptions) {
|
|
136
137
|
const tokenManager = new TokenManager();
|
|
@@ -145,16 +146,15 @@ export class FeedsClient extends FeedsApi {
|
|
|
145
146
|
this.state = new StateStore<FeedsClientState>({
|
|
146
147
|
connected_user: undefined,
|
|
147
148
|
is_ws_connection_healthy: false,
|
|
148
|
-
own_capabilities_by_fid: {},
|
|
149
149
|
});
|
|
150
150
|
this.moderation = new ModerationClient(apiClient);
|
|
151
151
|
this.tokenManager = tokenManager;
|
|
152
152
|
this.connectionIdManager = connectionIdManager;
|
|
153
153
|
this.polls_by_id = new Map();
|
|
154
154
|
|
|
155
|
-
this.
|
|
156
|
-
options?.
|
|
157
|
-
|
|
155
|
+
this.query_batch_own_fields_throttling_interval =
|
|
156
|
+
options?.query_batch_own_fields_throttling_interval ??
|
|
157
|
+
DEFAULT_BATCH_OWN_FIELDS_THROTTLING_INTERVAL;
|
|
158
158
|
|
|
159
159
|
feedsLoggerSystem.configureLoggers(options?.configure_loggers_options);
|
|
160
160
|
|
|
@@ -184,6 +184,7 @@ export class FeedsClient extends FeedsApi {
|
|
|
184
184
|
group: event.feed.group_id,
|
|
185
185
|
id: event.feed.id,
|
|
186
186
|
data: event.feed,
|
|
187
|
+
fieldsToUpdate: [],
|
|
187
188
|
});
|
|
188
189
|
|
|
189
190
|
break;
|
|
@@ -192,12 +193,9 @@ export class FeedsClient extends FeedsApi {
|
|
|
192
193
|
feeds.forEach((f) => f.handleWSEvent(event as unknown as WSEvent));
|
|
193
194
|
if (typeof fid === 'string') {
|
|
194
195
|
delete this.activeFeeds[fid];
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
delete this.activeActivities[activityId];
|
|
199
|
-
}
|
|
200
|
-
});
|
|
196
|
+
this.activeActivities = this.activeActivities.filter(
|
|
197
|
+
(activity) => getFeed.call(activity)?.feed !== fid,
|
|
198
|
+
);
|
|
201
199
|
}
|
|
202
200
|
break;
|
|
203
201
|
}
|
|
@@ -277,37 +275,36 @@ export class FeedsClient extends FeedsApi {
|
|
|
277
275
|
default: {
|
|
278
276
|
feeds.forEach((f) => f.handleWSEvent(event as unknown as WSEvent));
|
|
279
277
|
if (event.type === 'feeds.activity.deleted') {
|
|
280
|
-
|
|
278
|
+
this.activeActivities = this.activeActivities.filter(
|
|
279
|
+
(activity) => activity.id !== event.activity.id,
|
|
280
|
+
);
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
});
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
private
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
error_type: UnhandledErrorType.FetchingOwnCapabilitiesOnNewActivity,
|
|
301
|
-
error,
|
|
287
|
+
private setGetBatchOwnFieldsThrottlingInterval = (throttlingMs: number) => {
|
|
288
|
+
const { throttledFn: throttledGetBatchOwnFields, cancelTimer: cancel } =
|
|
289
|
+
throttle<GetBatchedOwnFieldsThrottledCallback>(
|
|
290
|
+
(feeds, callback) => {
|
|
291
|
+
this.ownBatch({
|
|
292
|
+
feeds,
|
|
293
|
+
}).catch((error) => {
|
|
294
|
+
this.eventDispatcher.dispatch({
|
|
295
|
+
type: 'errors.unhandled',
|
|
296
|
+
error_type:
|
|
297
|
+
UnhandledErrorType.FetchingOwnCapabilitiesOnNewActivity,
|
|
298
|
+
error,
|
|
299
|
+
});
|
|
302
300
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
this.
|
|
310
|
-
this.cancelGetBatchOwnCapabilitiesTimer = cancel;
|
|
301
|
+
callback(feeds);
|
|
302
|
+
},
|
|
303
|
+
throttlingMs,
|
|
304
|
+
{ trailing: true },
|
|
305
|
+
);
|
|
306
|
+
this.throttledGetBatchOwnFields = throttledGetBatchOwnFields;
|
|
307
|
+
this.cancelGetBatchOwnFieldsTimer = cancel;
|
|
311
308
|
};
|
|
312
309
|
|
|
313
310
|
private recoverOnReconnect = async () => {
|
|
@@ -316,18 +313,17 @@ export class FeedsClient extends FeedsApi {
|
|
|
316
313
|
// we skip the first event as we could potentially be querying twice
|
|
317
314
|
if (this.healthyConnectionChangedEventCount > 1) {
|
|
318
315
|
const feedEntries = Object.entries(this.activeFeeds);
|
|
319
|
-
const activityEntries = Object.entries(this.activeActivities);
|
|
320
316
|
|
|
321
317
|
const results = await Promise.allSettled([
|
|
322
318
|
...feedEntries.map(([, feed]) => feed.synchronize()),
|
|
323
|
-
...
|
|
319
|
+
...this.activeActivities.map((activity) => activity.synchronize()),
|
|
324
320
|
]);
|
|
325
321
|
|
|
326
322
|
const failures: SyncFailure[] = results.flatMap((result, index) => {
|
|
327
323
|
if (result.status === 'fulfilled') {
|
|
328
324
|
return [];
|
|
329
325
|
}
|
|
330
|
-
const activity =
|
|
326
|
+
const activity = this.activeActivities[index - feedEntries.length];
|
|
331
327
|
const feed =
|
|
332
328
|
feedEntries[index]?.[0] ?? (activity && getFeed.call(activity)?.feed);
|
|
333
329
|
|
|
@@ -369,34 +365,6 @@ export class FeedsClient extends FeedsApi {
|
|
|
369
365
|
}
|
|
370
366
|
}
|
|
371
367
|
|
|
372
|
-
public hydrateCapabilitiesCache(
|
|
373
|
-
feedResponses: Array<Pick<FeedResponse, 'feed' | 'own_capabilities'>>,
|
|
374
|
-
) {
|
|
375
|
-
let ownCapabilitiesCache =
|
|
376
|
-
this.state.getLatestValue().own_capabilities_by_fid;
|
|
377
|
-
|
|
378
|
-
const capabilitiesToFetchQueue: string[] = [];
|
|
379
|
-
|
|
380
|
-
for (const feedResponse of feedResponses) {
|
|
381
|
-
const { feed, own_capabilities } = feedResponse;
|
|
382
|
-
|
|
383
|
-
if (!Object.prototype.hasOwnProperty.call(ownCapabilitiesCache, feed)) {
|
|
384
|
-
if (own_capabilities) {
|
|
385
|
-
ownCapabilitiesCache = {
|
|
386
|
-
...ownCapabilitiesCache,
|
|
387
|
-
[feed]: own_capabilities,
|
|
388
|
-
};
|
|
389
|
-
} else {
|
|
390
|
-
capabilitiesToFetchQueue.push(feed);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
queueBatchedOwnCapabilities.bind(this)({ feeds: capabilitiesToFetchQueue });
|
|
396
|
-
|
|
397
|
-
this.state.partialNext({ own_capabilities_by_fid: ownCapabilitiesCache });
|
|
398
|
-
}
|
|
399
|
-
|
|
400
368
|
connectUser = async (user: UserRequest, tokenProvider?: TokenOrProvider) => {
|
|
401
369
|
if (
|
|
402
370
|
this.state.getLatestValue().connected_user !== undefined ||
|
|
@@ -407,8 +375,8 @@ export class FeedsClient extends FeedsApi {
|
|
|
407
375
|
|
|
408
376
|
this.tokenManager.setTokenOrProvider(tokenProvider);
|
|
409
377
|
|
|
410
|
-
this.
|
|
411
|
-
this.
|
|
378
|
+
this.setGetBatchOwnFieldsThrottlingInterval(
|
|
379
|
+
this.query_batch_own_fields_throttling_interval,
|
|
412
380
|
);
|
|
413
381
|
|
|
414
382
|
try {
|
|
@@ -643,16 +611,15 @@ export class FeedsClient extends FeedsApi {
|
|
|
643
611
|
// clear all caches
|
|
644
612
|
this.polls_by_id.clear();
|
|
645
613
|
|
|
646
|
-
this.activeActivities =
|
|
614
|
+
this.activeActivities = [];
|
|
647
615
|
this.activeFeeds = {};
|
|
648
616
|
|
|
649
617
|
this.state.partialNext({
|
|
650
618
|
connected_user: undefined,
|
|
651
619
|
is_ws_connection_healthy: false,
|
|
652
|
-
own_capabilities_by_fid: {},
|
|
653
620
|
});
|
|
654
621
|
|
|
655
|
-
this.
|
|
622
|
+
this.cancelGetBatchOwnFieldsTimer();
|
|
656
623
|
clearQueuedFeeds();
|
|
657
624
|
};
|
|
658
625
|
|
|
@@ -680,6 +647,7 @@ export class FeedsClient extends FeedsApi {
|
|
|
680
647
|
group: groupId,
|
|
681
648
|
id,
|
|
682
649
|
options,
|
|
650
|
+
fieldsToUpdate: [],
|
|
683
651
|
});
|
|
684
652
|
};
|
|
685
653
|
|
|
@@ -691,12 +659,14 @@ export class FeedsClient extends FeedsApi {
|
|
|
691
659
|
* @param id - The id of the activity
|
|
692
660
|
* @returns The activity with state updates
|
|
693
661
|
*/
|
|
694
|
-
activityWithStateUpdates = (
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
662
|
+
activityWithStateUpdates = (
|
|
663
|
+
id: ActivityId,
|
|
664
|
+
{ fromResponse }: { fromResponse?: ActivityResponse } = {
|
|
665
|
+
fromResponse: undefined,
|
|
666
|
+
},
|
|
667
|
+
) => {
|
|
668
|
+
const activity = new ActivityWithStateUpdates(id, this, { fromResponse });
|
|
669
|
+
this.activeActivities.push(activity);
|
|
700
670
|
return activity;
|
|
701
671
|
};
|
|
702
672
|
|
|
@@ -711,11 +681,15 @@ export class FeedsClient extends FeedsApi {
|
|
|
711
681
|
id: feedResponse.id,
|
|
712
682
|
data: feedResponse,
|
|
713
683
|
watch: request?.watch,
|
|
684
|
+
fieldsToUpdate: [
|
|
685
|
+
'own_capabilities',
|
|
686
|
+
'own_follows',
|
|
687
|
+
'own_membership',
|
|
688
|
+
'own_followings',
|
|
689
|
+
],
|
|
714
690
|
}),
|
|
715
691
|
);
|
|
716
692
|
|
|
717
|
-
this.hydrateCapabilitiesCache(feedResponses);
|
|
718
|
-
|
|
719
693
|
return {
|
|
720
694
|
feeds,
|
|
721
695
|
next: response.next,
|
|
@@ -727,13 +701,12 @@ export class FeedsClient extends FeedsApi {
|
|
|
727
701
|
|
|
728
702
|
async ownBatch(request: OwnBatchRequest) {
|
|
729
703
|
const response = await super.ownBatch(request);
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
735
|
-
);
|
|
736
|
-
this.hydrateCapabilitiesCache(feedResponses);
|
|
704
|
+
Object.entries(response.data).forEach(([fid, ownFields]) => {
|
|
705
|
+
const feed = this.activeFeeds[fid];
|
|
706
|
+
if (feed) {
|
|
707
|
+
feed.state.partialNext(ownFields);
|
|
708
|
+
}
|
|
709
|
+
});
|
|
737
710
|
return response;
|
|
738
711
|
}
|
|
739
712
|
|
|
@@ -826,7 +799,6 @@ export class FeedsClient extends FeedsApi {
|
|
|
826
799
|
},
|
|
827
800
|
) {
|
|
828
801
|
const response = await super.getOrCreateFeed(request);
|
|
829
|
-
this.hydrateCapabilitiesCache([response.feed]);
|
|
830
802
|
|
|
831
803
|
if (request.watch) {
|
|
832
804
|
const feeds = this.findAllActiveFeedsByFid(
|
|
@@ -840,19 +812,24 @@ export class FeedsClient extends FeedsApi {
|
|
|
840
812
|
|
|
841
813
|
async getFollowSuggestions(
|
|
842
814
|
...params: Parameters<FeedsApi['getFollowSuggestions']>
|
|
843
|
-
): Promise<StreamResponse<GetFollowSuggestionsResponse>> {
|
|
815
|
+
): Promise<StreamResponse<GetFollowSuggestionsResponse & { feeds: Feed[] }>> {
|
|
844
816
|
const response = await super.getFollowSuggestions(...params);
|
|
845
817
|
|
|
846
|
-
response.suggestions.
|
|
847
|
-
this.getOrCreateActiveFeed({
|
|
818
|
+
const feeds = response.suggestions.map((suggestion) => {
|
|
819
|
+
return this.getOrCreateActiveFeed({
|
|
848
820
|
group: suggestion.group_id,
|
|
849
821
|
id: suggestion.id,
|
|
850
822
|
data: suggestion,
|
|
823
|
+
fieldsToUpdate: [
|
|
824
|
+
'own_capabilities',
|
|
825
|
+
'own_follows',
|
|
826
|
+
'own_membership',
|
|
827
|
+
'own_followings',
|
|
828
|
+
],
|
|
851
829
|
});
|
|
852
830
|
});
|
|
853
831
|
|
|
854
|
-
|
|
855
|
-
return response;
|
|
832
|
+
return { ...response, feeds };
|
|
856
833
|
}
|
|
857
834
|
|
|
858
835
|
protected readonly getOrCreateActiveFeed = ({
|
|
@@ -861,7 +838,7 @@ export class FeedsClient extends FeedsApi {
|
|
|
861
838
|
data,
|
|
862
839
|
watch,
|
|
863
840
|
options,
|
|
864
|
-
|
|
841
|
+
fieldsToUpdate,
|
|
865
842
|
}: {
|
|
866
843
|
group: string;
|
|
867
844
|
id: string;
|
|
@@ -871,7 +848,9 @@ export class FeedsClient extends FeedsApi {
|
|
|
871
848
|
addNewActivitiesTo?: 'start' | 'end';
|
|
872
849
|
activityAddedEventFilter?: (event: ActivityAddedEvent) => boolean;
|
|
873
850
|
};
|
|
874
|
-
|
|
851
|
+
fieldsToUpdate: Array<
|
|
852
|
+
'own_capabilities' | 'own_follows' | 'own_followings' | 'own_membership'
|
|
853
|
+
>;
|
|
875
854
|
}) => {
|
|
876
855
|
const fid = `${group}:${id}`;
|
|
877
856
|
let isCreated = false;
|
|
@@ -909,18 +888,35 @@ export class FeedsClient extends FeedsApi {
|
|
|
909
888
|
handleFeedUpdated.call(feed, { feed: data });
|
|
910
889
|
} else if (
|
|
911
890
|
(feed.currentState.updated_at?.getTime() ?? 0) ===
|
|
912
|
-
|
|
913
|
-
!fromWebSocket
|
|
891
|
+
data.updated_at.getTime()
|
|
914
892
|
) {
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
893
|
+
const fieldsToUpdateData: Array<keyof FeedResponse> = [];
|
|
894
|
+
const fieldChecks: Array<
|
|
895
|
+
[
|
|
896
|
+
(
|
|
897
|
+
| 'own_capabilities'
|
|
898
|
+
| 'own_follows'
|
|
899
|
+
| 'own_membership'
|
|
900
|
+
| 'own_followings'
|
|
901
|
+
),
|
|
902
|
+
(currentState: FeedState, newState: FeedResponse) => boolean,
|
|
903
|
+
]
|
|
904
|
+
> = [
|
|
905
|
+
['own_capabilities', isOwnCapabilitiesEqual],
|
|
906
|
+
['own_follows', isOwnFollowsEqual],
|
|
907
|
+
['own_membership', isOwnMembershipEqual],
|
|
908
|
+
['own_followings', isOwnFollowingsEqual],
|
|
909
|
+
];
|
|
910
|
+
fieldChecks.forEach(([field, isEqual]) => {
|
|
911
|
+
if (
|
|
912
|
+
fieldsToUpdate.includes(field) &&
|
|
913
|
+
!isEqual(feed.currentState, data)
|
|
914
|
+
) {
|
|
915
|
+
fieldsToUpdateData.push(field);
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
if (fieldsToUpdateData.length > 0) {
|
|
919
|
+
const fieldsToUpdatePayload = fieldsToUpdateData.reduce(
|
|
924
920
|
(acc: Partial<FeedResponse>, field) => {
|
|
925
921
|
// @ts-expect-error TODO: fix this
|
|
926
922
|
acc[field] = data[field];
|
|
@@ -928,7 +924,7 @@ export class FeedsClient extends FeedsApi {
|
|
|
928
924
|
},
|
|
929
925
|
{},
|
|
930
926
|
);
|
|
931
|
-
feed.state.partialNext(
|
|
927
|
+
feed.state.partialNext(fieldsToUpdatePayload);
|
|
932
928
|
}
|
|
933
929
|
}
|
|
934
930
|
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import type { FeedState } from '../feed';
|
|
2
|
-
import type { FeedResponse } from '../gen/models';
|
|
2
|
+
import type { FeedResponse, FollowResponse } from '../gen/models';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
) => {
|
|
4
|
+
const areFollowArraysEqual = (
|
|
5
|
+
currentFollows: FollowResponse[] | undefined,
|
|
6
|
+
newFollows: FollowResponse[] | undefined,
|
|
7
|
+
): boolean => {
|
|
8
8
|
const existingFollows = new Set(
|
|
9
|
-
|
|
9
|
+
currentFollows?.map(
|
|
10
10
|
(f) =>
|
|
11
11
|
`${f.source_feed.feed}:${f.target_feed.feed}:${f.updated_at.getTime()}`,
|
|
12
12
|
),
|
|
13
13
|
);
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
const newFollowsSet = new Set(
|
|
15
|
+
newFollows?.map(
|
|
16
16
|
(f) =>
|
|
17
17
|
`${f.source_feed.feed}:${f.target_feed.feed}:${f.updated_at.getTime()}`,
|
|
18
18
|
),
|
|
19
19
|
);
|
|
20
|
-
if (existingFollows.size ===
|
|
20
|
+
if (existingFollows.size === newFollowsSet.size) {
|
|
21
21
|
const areEqual = Array.from(existingFollows).every((f) =>
|
|
22
|
-
|
|
22
|
+
newFollowsSet.has(f),
|
|
23
23
|
);
|
|
24
24
|
if (areEqual) {
|
|
25
25
|
return true;
|
|
@@ -29,6 +29,13 @@ export const isOwnFollowsEqual = (
|
|
|
29
29
|
return false;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
export const isOwnFollowsEqual = (
|
|
33
|
+
currentState: FeedState,
|
|
34
|
+
newState: FeedResponse,
|
|
35
|
+
) => {
|
|
36
|
+
return areFollowArraysEqual(currentState.own_follows, newState.own_follows);
|
|
37
|
+
};
|
|
38
|
+
|
|
32
39
|
export const isOwnMembershipEqual = (
|
|
33
40
|
currentState: FeedState,
|
|
34
41
|
newState: FeedResponse,
|
|
@@ -38,3 +45,23 @@ export const isOwnMembershipEqual = (
|
|
|
38
45
|
(newState.own_membership?.updated_at.getTime() ?? 0)
|
|
39
46
|
);
|
|
40
47
|
};
|
|
48
|
+
|
|
49
|
+
export const isOwnCapabilitiesEqual = (
|
|
50
|
+
currentState: FeedState,
|
|
51
|
+
newState: FeedResponse,
|
|
52
|
+
) => {
|
|
53
|
+
return (
|
|
54
|
+
[...(currentState.own_capabilities ?? [])].sort().join(',') ===
|
|
55
|
+
[...(newState.own_capabilities ?? [])].sort().join(',')
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const isOwnFollowingsEqual = (
|
|
60
|
+
currentState: FeedState,
|
|
61
|
+
newState: FeedResponse,
|
|
62
|
+
) => {
|
|
63
|
+
return areFollowArraysEqual(
|
|
64
|
+
currentState.own_followings,
|
|
65
|
+
newState.own_followings,
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './throttle';
|
|
2
|
-
export * from './throttled-get-batched-own-
|
|
2
|
+
export * from './throttled-get-batched-own-fields';
|