@stream-io/feeds-client 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@react-bindings/hooks/client-state-hooks/index.ts +2 -0
- package/@react-bindings/hooks/feed-state-hooks/index.ts +7 -0
- package/@react-bindings/hooks/internal/index.ts +1 -0
- package/@react-bindings/hooks/util/index.ts +1 -0
- package/@react-bindings/index.ts +6 -3
- package/CHANGELOG.md +31 -0
- package/dist/@react-bindings/contexts/StreamFeedContext.d.ts +12 -0
- package/dist/@react-bindings/hooks/client-state-hooks/index.d.ts +2 -0
- package/dist/@react-bindings/hooks/client-state-hooks/useClientConnectedUser.d.ts +4 -0
- package/dist/@react-bindings/hooks/client-state-hooks/useWsConnectionState.d.ts +6 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/index.d.ts +7 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useComments.d.ts +19 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useFeedActivities.d.ts +11 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useFeedMetadata.d.ts +12 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useFollowers.d.ts +16 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useFollowing.d.ts +16 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useOwnCapabilities.d.ts +33 -0
- package/dist/@react-bindings/hooks/feed-state-hooks/useOwnFollows.d.ts +8 -0
- package/dist/@react-bindings/hooks/internal/index.d.ts +1 -0
- package/dist/@react-bindings/hooks/internal/useStableCallback.d.ts +25 -0
- package/dist/@react-bindings/hooks/util/index.d.ts +1 -0
- package/dist/@react-bindings/hooks/util/useReactionActions.d.ts +17 -0
- package/dist/@react-bindings/index.d.ts +5 -3
- package/dist/@react-bindings/wrappers/StreamFeed.d.ts +12 -0
- package/dist/index-react-bindings.browser.cjs +521 -210
- package/dist/index-react-bindings.browser.cjs.map +1 -1
- package/dist/index-react-bindings.browser.js +514 -212
- package/dist/index-react-bindings.browser.js.map +1 -1
- package/dist/index-react-bindings.node.cjs +521 -210
- package/dist/index-react-bindings.node.cjs.map +1 -1
- package/dist/index-react-bindings.node.js +514 -212
- package/dist/index-react-bindings.node.js.map +1 -1
- package/dist/index.browser.cjs +212 -106
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +209 -107
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +212 -106
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +209 -107
- package/dist/index.node.js.map +1 -1
- package/dist/src/Feed.d.ts +7 -3
- package/dist/src/FeedsClient.d.ts +6 -5
- package/dist/src/types.d.ts +7 -0
- package/dist/src/utils.d.ts +9 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -2
- package/src/Feed.ts +214 -97
- package/src/FeedsClient.ts +22 -12
- package/src/common/ActivitySearchSource.ts +5 -5
- package/src/common/ApiClient.ts +1 -1
- package/src/common/FeedSearchSource.ts +1 -1
- package/src/common/Poll.ts +35 -10
- package/src/common/TokenManager.ts +2 -3
- package/src/common/UserSearchSource.ts +1 -1
- package/src/common/real-time/StableWSConnection.ts +4 -1
- package/src/state-updates/bookmark-utils.test.ts +134 -8
- package/src/state-updates/bookmark-utils.ts +17 -7
- package/src/types.ts +12 -1
- package/src/utils.ts +25 -1
- package/dist/@react-bindings/hooks/clientStateHooks.d.ts +0 -10
- package/dist/@react-bindings/hooks/useComments.d.ts +0 -12
- package/dist/@react-bindings/hooks/useOwnCapabilities.d.ts +0 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/feeds-client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"packageManager": "yarn@3.2.4",
|
|
5
5
|
"main": "./dist/index.node.js",
|
|
6
6
|
"exports": {
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
"start": "rollup -w -c",
|
|
45
45
|
"build": "yarn clean && rollup -c",
|
|
46
46
|
"test": "vitest",
|
|
47
|
-
"test
|
|
47
|
+
"test:unit": "vitest --exclude '__integration-tests__/**'",
|
|
48
|
+
"test-ci": "vitest --exclude '__integration-tests__/docs-snippets/**' --coverage",
|
|
49
|
+
"test-docs-snippets": "vitest __integration-tests__/docs-snippets/**"
|
|
48
50
|
},
|
|
49
51
|
"files": [
|
|
50
52
|
"dist",
|
|
@@ -61,7 +63,9 @@
|
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"@rollup/plugin-replace": "^6.0.1",
|
|
63
65
|
"@rollup/plugin-typescript": "^12.1.0",
|
|
66
|
+
"@stream-io/node-sdk": "0.5.0",
|
|
64
67
|
"@types/react": "^19.1.8",
|
|
68
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
65
69
|
"dotenv": "^16.4.5",
|
|
66
70
|
"react": "19.0.0",
|
|
67
71
|
"rimraf": "^6.0.1",
|
package/src/Feed.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
BookmarkAddedEvent,
|
|
15
15
|
BookmarkDeletedEvent,
|
|
16
16
|
BookmarkUpdatedEvent,
|
|
17
|
+
QueryFeedMembersRequest,
|
|
18
|
+
SortParamRequest,
|
|
17
19
|
} from './gen/models';
|
|
18
20
|
import { Patch, StateStore } from './common/StateStore';
|
|
19
21
|
import { EventDispatcher } from './common/EventDispatcher';
|
|
@@ -43,6 +45,7 @@ import type {
|
|
|
43
45
|
PagerResponseWithLoadingStates,
|
|
44
46
|
} from './types';
|
|
45
47
|
import type { FromArray } from './types-internal';
|
|
48
|
+
import { checkHasAnotherPage, Constants } from './utils';
|
|
46
49
|
|
|
47
50
|
export type FeedState = Omit<
|
|
48
51
|
Partial<GetOrCreateFeedResponse & FeedResponse>,
|
|
@@ -103,16 +106,15 @@ export type FeedState = Omit<
|
|
|
103
106
|
| undefined
|
|
104
107
|
>;
|
|
105
108
|
|
|
106
|
-
followers_pagination?: LoadingStates & { sort?:
|
|
109
|
+
followers_pagination?: LoadingStates & { sort?: SortParamRequest[] };
|
|
107
110
|
|
|
108
|
-
following_pagination?: LoadingStates & { sort?:
|
|
111
|
+
following_pagination?: LoadingStates & { sort?: SortParamRequest[] };
|
|
112
|
+
|
|
113
|
+
member_pagination?: LoadingStates & { sort?: SortParamRequest[] };
|
|
109
114
|
|
|
110
115
|
last_get_or_create_request_config?: GetOrCreateFeedRequest;
|
|
111
116
|
};
|
|
112
117
|
|
|
113
|
-
const END_OF_LIST = 'eol' as const;
|
|
114
|
-
const DEFAULT_COMMENT_PAGINATION = 'first' as const;
|
|
115
|
-
|
|
116
118
|
type EventHandlerByEventType = {
|
|
117
119
|
[Key in NonNullable<WSEvent['type']>]: Key extends Extract<
|
|
118
120
|
WSEvent,
|
|
@@ -153,7 +155,7 @@ export class Feed extends FeedApi {
|
|
|
153
155
|
},
|
|
154
156
|
'feeds.activity.reaction.added': (event) => {
|
|
155
157
|
const currentActivities = this.currentState.activities;
|
|
156
|
-
const connectedUser = this.client.state.getLatestValue().
|
|
158
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
157
159
|
const isCurrentUser = Boolean(
|
|
158
160
|
connectedUser && event.reaction.user.id === connectedUser.id,
|
|
159
161
|
);
|
|
@@ -169,7 +171,7 @@ export class Feed extends FeedApi {
|
|
|
169
171
|
},
|
|
170
172
|
'feeds.activity.reaction.deleted': (event) => {
|
|
171
173
|
const currentActivities = this.currentState.activities;
|
|
172
|
-
const connectedUser = this.client.state.getLatestValue().
|
|
174
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
173
175
|
const isCurrentUser = Boolean(
|
|
174
176
|
connectedUser && event.reaction.user.id === connectedUser.id,
|
|
175
177
|
);
|
|
@@ -221,7 +223,10 @@ export class Feed extends FeedApi {
|
|
|
221
223
|
|
|
222
224
|
if (
|
|
223
225
|
entityState?.pagination?.sort === 'last' &&
|
|
224
|
-
|
|
226
|
+
!checkHasAnotherPage(
|
|
227
|
+
entityState.comments,
|
|
228
|
+
entityState?.pagination.next,
|
|
229
|
+
)
|
|
225
230
|
) {
|
|
226
231
|
newComments.unshift(comment);
|
|
227
232
|
} else if (entityState?.pagination?.sort === 'first') {
|
|
@@ -320,7 +325,12 @@ export class Feed extends FeedApi {
|
|
|
320
325
|
...event.follow.source_feed,
|
|
321
326
|
};
|
|
322
327
|
|
|
323
|
-
if (
|
|
328
|
+
if (
|
|
329
|
+
!checkHasAnotherPage(
|
|
330
|
+
currentState.following,
|
|
331
|
+
currentState.following_pagination?.next,
|
|
332
|
+
)
|
|
333
|
+
) {
|
|
324
334
|
// TODO: respect sort
|
|
325
335
|
newState.following = currentState.following
|
|
326
336
|
? currentState.following.concat(event.follow)
|
|
@@ -334,7 +344,7 @@ export class Feed extends FeedApi {
|
|
|
334
344
|
event.follow.target_feed.fid === this.fid
|
|
335
345
|
) {
|
|
336
346
|
const source = event.follow.source_feed;
|
|
337
|
-
const connectedUser = this.client.state.getLatestValue().
|
|
347
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
338
348
|
|
|
339
349
|
this.state.next((currentState) => {
|
|
340
350
|
const newState = { ...currentState, ...event.follow.target_feed };
|
|
@@ -345,7 +355,12 @@ export class Feed extends FeedApi {
|
|
|
345
355
|
: [event.follow];
|
|
346
356
|
}
|
|
347
357
|
|
|
348
|
-
if (
|
|
358
|
+
if (
|
|
359
|
+
!checkHasAnotherPage(
|
|
360
|
+
currentState.followers,
|
|
361
|
+
currentState.followers_pagination?.next,
|
|
362
|
+
)
|
|
363
|
+
) {
|
|
349
364
|
// TODO: respect sort
|
|
350
365
|
newState.followers = currentState.followers
|
|
351
366
|
? currentState.followers.concat(event.follow)
|
|
@@ -374,7 +389,7 @@ export class Feed extends FeedApi {
|
|
|
374
389
|
event.follow.target_feed.fid === this.fid
|
|
375
390
|
) {
|
|
376
391
|
const source = event.follow.source_feed;
|
|
377
|
-
const connectedUser = this.client.state.getLatestValue().
|
|
392
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
378
393
|
|
|
379
394
|
this.state.next((currentState) => {
|
|
380
395
|
const newState = { ...currentState, ...event.follow.target_feed };
|
|
@@ -400,49 +415,90 @@ export class Feed extends FeedApi {
|
|
|
400
415
|
this.handleCommentReactionEvent.bind(this),
|
|
401
416
|
'feeds.comment.reaction.updated': Feed.noop,
|
|
402
417
|
'feeds.feed_member.added': (event) => {
|
|
403
|
-
const {
|
|
404
|
-
|
|
405
|
-
// do not add a member if the pagination has reached the end of the list
|
|
406
|
-
if (this.currentState.member_pagination?.next !== END_OF_LIST) return;
|
|
418
|
+
const { connected_user: connectedUser } =
|
|
419
|
+
this.client.state.getLatestValue();
|
|
407
420
|
|
|
408
421
|
this.state.next((currentState) => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
422
|
+
let newState: FeedState | undefined;
|
|
423
|
+
|
|
424
|
+
if (
|
|
425
|
+
!checkHasAnotherPage(
|
|
426
|
+
currentState.members,
|
|
427
|
+
currentState.member_pagination?.next,
|
|
428
|
+
)
|
|
429
|
+
) {
|
|
430
|
+
newState ??= {
|
|
431
|
+
...currentState,
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
newState.members = newState.members?.concat(event.member) ?? [
|
|
435
|
+
event.member,
|
|
436
|
+
];
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (connectedUser?.id === event.member.user.id) {
|
|
440
|
+
newState ??= {
|
|
441
|
+
...currentState,
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
newState.own_membership = event.member;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return newState ?? currentState;
|
|
416
448
|
});
|
|
417
449
|
},
|
|
418
450
|
'feeds.feed_member.removed': (event) => {
|
|
451
|
+
const { connected_user: connectedUser } =
|
|
452
|
+
this.client.state.getLatestValue();
|
|
453
|
+
|
|
419
454
|
this.state.next((currentState) => {
|
|
420
|
-
|
|
455
|
+
const newState = {
|
|
421
456
|
...currentState,
|
|
422
457
|
members: currentState.members?.filter(
|
|
423
458
|
(member) => member.user.id !== event.user?.id,
|
|
424
459
|
),
|
|
425
460
|
};
|
|
461
|
+
|
|
462
|
+
if (connectedUser?.id === event.member_id) {
|
|
463
|
+
delete newState.own_membership;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return newState;
|
|
426
467
|
});
|
|
427
468
|
},
|
|
428
469
|
'feeds.feed_member.updated': (event) => {
|
|
470
|
+
const { connected_user: connectedUser } =
|
|
471
|
+
this.client.state.getLatestValue();
|
|
472
|
+
|
|
429
473
|
this.state.next((currentState) => {
|
|
430
474
|
const memberIndex =
|
|
431
475
|
currentState.members?.findIndex(
|
|
432
476
|
(member) => member.user.id === event.member.user.id,
|
|
433
477
|
) ?? -1;
|
|
434
478
|
|
|
479
|
+
let newState: FeedState | undefined;
|
|
480
|
+
|
|
435
481
|
if (memberIndex !== -1) {
|
|
482
|
+
// if there's an index, there's a member to update
|
|
436
483
|
const newMembers = [...currentState.members!];
|
|
437
484
|
newMembers[memberIndex] = event.member;
|
|
438
485
|
|
|
439
|
-
|
|
486
|
+
newState ??= {
|
|
487
|
+
...currentState,
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
newState.members = newMembers;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (connectedUser?.id === event.member.user.id) {
|
|
494
|
+
newState ??= {
|
|
440
495
|
...currentState,
|
|
441
|
-
members: newMembers,
|
|
442
496
|
};
|
|
497
|
+
|
|
498
|
+
newState.own_membership = event.member;
|
|
443
499
|
}
|
|
444
500
|
|
|
445
|
-
return currentState;
|
|
501
|
+
return newState ?? currentState;
|
|
446
502
|
});
|
|
447
503
|
},
|
|
448
504
|
// the poll events should be removed from here
|
|
@@ -506,7 +562,7 @@ export class Feed extends FeedApi {
|
|
|
506
562
|
},
|
|
507
563
|
) {
|
|
508
564
|
const { comment, reaction } = event;
|
|
509
|
-
const connectedUser = this.client.state.getLatestValue().
|
|
565
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
510
566
|
|
|
511
567
|
this.state.next((currentState) => {
|
|
512
568
|
const forId = comment.parent_id ?? comment.object_id;
|
|
@@ -614,38 +670,6 @@ export class Feed extends FeedApi {
|
|
|
614
670
|
...responseCopy,
|
|
615
671
|
};
|
|
616
672
|
|
|
617
|
-
// if there is no next cursor, set it to END_OF_LIST
|
|
618
|
-
// request has to have a limit set for this to work
|
|
619
|
-
if (
|
|
620
|
-
(request?.followers_pagination?.limit ?? 0) > 0 &&
|
|
621
|
-
typeof nextState.followers_pagination?.next === 'undefined'
|
|
622
|
-
) {
|
|
623
|
-
nextState.followers_pagination = {
|
|
624
|
-
...nextState.followers_pagination,
|
|
625
|
-
next: END_OF_LIST,
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
if (
|
|
630
|
-
(request?.following_pagination?.limit ?? 0) > 0 &&
|
|
631
|
-
typeof nextState.following_pagination?.next === 'undefined'
|
|
632
|
-
) {
|
|
633
|
-
nextState.following_pagination = {
|
|
634
|
-
...nextState.following_pagination,
|
|
635
|
-
next: END_OF_LIST,
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
if (
|
|
640
|
-
(request?.member_pagination?.limit ?? 0) > 0 &&
|
|
641
|
-
typeof nextState.member_pagination?.next === 'undefined'
|
|
642
|
-
) {
|
|
643
|
-
nextState.member_pagination = {
|
|
644
|
-
...nextState.member_pagination,
|
|
645
|
-
next: END_OF_LIST,
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
|
|
649
673
|
if (!request?.followers_pagination?.limit) {
|
|
650
674
|
delete nextState.followers;
|
|
651
675
|
}
|
|
@@ -672,7 +696,8 @@ export class Feed extends FeedApi {
|
|
|
672
696
|
|
|
673
697
|
private handleBookmarkAdded(event: BookmarkAddedEvent) {
|
|
674
698
|
const currentActivities = this.currentState.activities;
|
|
675
|
-
const { connectedUser } =
|
|
699
|
+
const { connected_user: connectedUser } =
|
|
700
|
+
this.client.state.getLatestValue();
|
|
676
701
|
const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
|
|
677
702
|
|
|
678
703
|
const result = addBookmarkToActivities(
|
|
@@ -687,7 +712,8 @@ export class Feed extends FeedApi {
|
|
|
687
712
|
|
|
688
713
|
private handleBookmarkDeleted(event: BookmarkDeletedEvent) {
|
|
689
714
|
const currentActivities = this.currentState.activities;
|
|
690
|
-
const { connectedUser } =
|
|
715
|
+
const { connected_user: connectedUser } =
|
|
716
|
+
this.client.state.getLatestValue();
|
|
691
717
|
const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
|
|
692
718
|
|
|
693
719
|
const result = removeBookmarkFromActivities(
|
|
@@ -702,7 +728,8 @@ export class Feed extends FeedApi {
|
|
|
702
728
|
|
|
703
729
|
private handleBookmarkUpdated(event: BookmarkUpdatedEvent) {
|
|
704
730
|
const currentActivities = this.currentState.activities;
|
|
705
|
-
const { connectedUser } =
|
|
731
|
+
const { connected_user: connectedUser } =
|
|
732
|
+
this.client.state.getLatestValue();
|
|
706
733
|
const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
|
|
707
734
|
|
|
708
735
|
const result = updateBookmarkInActivities(
|
|
@@ -796,6 +823,8 @@ export class Feed extends FeedApi {
|
|
|
796
823
|
sort: string;
|
|
797
824
|
base: () => Promise<PagerResponse & { comments: CommentResponse[] }>;
|
|
798
825
|
}) {
|
|
826
|
+
let error: unknown;
|
|
827
|
+
|
|
799
828
|
try {
|
|
800
829
|
this.state.next((currentState) => ({
|
|
801
830
|
...currentState,
|
|
@@ -811,7 +840,7 @@ export class Feed extends FeedApi {
|
|
|
811
840
|
},
|
|
812
841
|
}));
|
|
813
842
|
|
|
814
|
-
const { next: newNextCursor
|
|
843
|
+
const { next: newNextCursor, comments } = await base();
|
|
815
844
|
|
|
816
845
|
this.state.next((currentState) => {
|
|
817
846
|
const newPagination = {
|
|
@@ -840,9 +869,8 @@ export class Feed extends FeedApi {
|
|
|
840
869
|
},
|
|
841
870
|
};
|
|
842
871
|
});
|
|
843
|
-
} catch (
|
|
844
|
-
|
|
845
|
-
// TODO: figure out how to handle errorss
|
|
872
|
+
} catch (e) {
|
|
873
|
+
error = e;
|
|
846
874
|
} finally {
|
|
847
875
|
this.state.next((currentState) => ({
|
|
848
876
|
...currentState,
|
|
@@ -858,6 +886,10 @@ export class Feed extends FeedApi {
|
|
|
858
886
|
},
|
|
859
887
|
}));
|
|
860
888
|
}
|
|
889
|
+
|
|
890
|
+
if (error) {
|
|
891
|
+
throw error;
|
|
892
|
+
}
|
|
861
893
|
}
|
|
862
894
|
|
|
863
895
|
public async loadNextPageActivityComments(
|
|
@@ -866,15 +898,22 @@ export class Feed extends FeedApi {
|
|
|
866
898
|
Omit<GetCommentsRequest, 'object_id' | 'object_type' | 'next'>
|
|
867
899
|
>,
|
|
868
900
|
) {
|
|
869
|
-
const
|
|
870
|
-
this.currentState.comments_by_entity_id[activity.id]
|
|
871
|
-
const
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
901
|
+
const currentEntityState =
|
|
902
|
+
this.currentState.comments_by_entity_id[activity.id];
|
|
903
|
+
const currentPagination = currentEntityState?.pagination;
|
|
904
|
+
const currentNextCursor = currentPagination?.next;
|
|
905
|
+
const currentSort = currentPagination?.sort;
|
|
906
|
+
const isLoading = currentPagination?.loading_next_page;
|
|
907
|
+
|
|
908
|
+
const sort =
|
|
909
|
+
currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
|
|
910
|
+
|
|
911
|
+
if (
|
|
912
|
+
isLoading ||
|
|
913
|
+
!checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)
|
|
914
|
+
) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
878
917
|
|
|
879
918
|
await this.loadNextPageComments({
|
|
880
919
|
forId: activity.id,
|
|
@@ -894,15 +933,22 @@ export class Feed extends FeedApi {
|
|
|
894
933
|
comment: CommentResponse,
|
|
895
934
|
request?: Partial<Omit<GetCommentsRepliesRequest, 'comment_id' | 'next'>>,
|
|
896
935
|
) {
|
|
897
|
-
const
|
|
898
|
-
this.currentState.comments_by_entity_id[comment.id]
|
|
899
|
-
const
|
|
900
|
-
const
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
936
|
+
const currentEntityState =
|
|
937
|
+
this.currentState.comments_by_entity_id[comment.id];
|
|
938
|
+
const currentPagination = currentEntityState?.pagination;
|
|
939
|
+
const currentNextCursor = currentPagination?.next;
|
|
940
|
+
const currentSort = currentPagination?.sort;
|
|
941
|
+
const isLoading = currentPagination?.loading_next_page;
|
|
942
|
+
|
|
943
|
+
const sort =
|
|
944
|
+
currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
|
|
945
|
+
|
|
946
|
+
if (
|
|
947
|
+
isLoading ||
|
|
948
|
+
!checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)
|
|
949
|
+
) {
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
906
952
|
|
|
907
953
|
await this.loadNextPageComments({
|
|
908
954
|
forId: comment.id,
|
|
@@ -911,7 +957,10 @@ export class Feed extends FeedApi {
|
|
|
911
957
|
...request,
|
|
912
958
|
comment_id: comment.id,
|
|
913
959
|
// use known sort first (prevents broken pagination)
|
|
914
|
-
sort:
|
|
960
|
+
sort:
|
|
961
|
+
currentSort ??
|
|
962
|
+
request?.sort ??
|
|
963
|
+
Constants.DEFAULT_COMMENT_PAGINATION,
|
|
915
964
|
next: currentNextCursor,
|
|
916
965
|
}),
|
|
917
966
|
parentId: comment.parent_id ?? comment.object_id,
|
|
@@ -921,15 +970,20 @@ export class Feed extends FeedApi {
|
|
|
921
970
|
|
|
922
971
|
private async loadNextPageFollows(
|
|
923
972
|
type: 'followers' | 'following',
|
|
924
|
-
request: Pick<QueryFollowsRequest, 'limit'>,
|
|
973
|
+
request: Pick<QueryFollowsRequest, 'limit' | 'sort'>,
|
|
925
974
|
) {
|
|
926
975
|
const paginationKey = `${type}_pagination` as const;
|
|
927
976
|
const method = `query${capitalize(type)}` as const;
|
|
928
977
|
|
|
978
|
+
const currentFollows = this.currentState[type];
|
|
929
979
|
const currentNextCursor = this.currentState[paginationKey]?.next;
|
|
930
980
|
const isLoading = this.currentState[paginationKey]?.loading_next_page;
|
|
981
|
+
const sort = this.currentState[paginationKey]?.sort ?? request.sort;
|
|
982
|
+
let error: unknown;
|
|
931
983
|
|
|
932
|
-
if (isLoading || currentNextCursor
|
|
984
|
+
if (isLoading || !checkHasAnotherPage(currentFollows, currentNextCursor)) {
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
933
987
|
|
|
934
988
|
try {
|
|
935
989
|
this.state.next((currentState) => {
|
|
@@ -942,12 +996,11 @@ export class Feed extends FeedApi {
|
|
|
942
996
|
};
|
|
943
997
|
});
|
|
944
998
|
|
|
945
|
-
const { next: newNextCursor
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
);
|
|
999
|
+
const { next: newNextCursor, follows } = await this[method]({
|
|
1000
|
+
...request,
|
|
1001
|
+
next: currentNextCursor,
|
|
1002
|
+
sort,
|
|
1003
|
+
});
|
|
951
1004
|
|
|
952
1005
|
this.state.next((currentState) => ({
|
|
953
1006
|
...currentState,
|
|
@@ -957,11 +1010,11 @@ export class Feed extends FeedApi {
|
|
|
957
1010
|
[paginationKey]: {
|
|
958
1011
|
...currentState[paginationKey],
|
|
959
1012
|
next: newNextCursor,
|
|
1013
|
+
sort,
|
|
960
1014
|
},
|
|
961
1015
|
}));
|
|
962
|
-
} catch (
|
|
963
|
-
|
|
964
|
-
// TODO: figure out how to handle errorss
|
|
1016
|
+
} catch (e) {
|
|
1017
|
+
error = e;
|
|
965
1018
|
} finally {
|
|
966
1019
|
this.state.next((currentState) => {
|
|
967
1020
|
return {
|
|
@@ -973,6 +1026,10 @@ export class Feed extends FeedApi {
|
|
|
973
1026
|
};
|
|
974
1027
|
});
|
|
975
1028
|
}
|
|
1029
|
+
|
|
1030
|
+
if (error) {
|
|
1031
|
+
throw error;
|
|
1032
|
+
}
|
|
976
1033
|
}
|
|
977
1034
|
|
|
978
1035
|
async loadNextPageFollowers(request: Pick<QueryFollowsRequest, 'limit'>) {
|
|
@@ -983,6 +1040,66 @@ export class Feed extends FeedApi {
|
|
|
983
1040
|
await this.loadNextPageFollows('following', request);
|
|
984
1041
|
}
|
|
985
1042
|
|
|
1043
|
+
async loadNextPageMembers(
|
|
1044
|
+
request: Omit<QueryFeedMembersRequest, 'next' | 'prev'>,
|
|
1045
|
+
) {
|
|
1046
|
+
const currentMembers = this.currentState.members;
|
|
1047
|
+
const currentNextCursor = this.currentState.member_pagination?.next;
|
|
1048
|
+
const isLoading = this.currentState.member_pagination?.loading_next_page;
|
|
1049
|
+
const sort = this.currentState.member_pagination?.sort ?? request.sort;
|
|
1050
|
+
let error: unknown;
|
|
1051
|
+
|
|
1052
|
+
if (isLoading || !checkHasAnotherPage(currentMembers, currentNextCursor)) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
try {
|
|
1057
|
+
this.state.next((currentState) => ({
|
|
1058
|
+
...currentState,
|
|
1059
|
+
member_pagination: {
|
|
1060
|
+
...currentState.member_pagination,
|
|
1061
|
+
loading_next_page: true,
|
|
1062
|
+
},
|
|
1063
|
+
}));
|
|
1064
|
+
|
|
1065
|
+
const { next: newNextCursor, members } =
|
|
1066
|
+
await this.client.queryFeedMembers({
|
|
1067
|
+
...request,
|
|
1068
|
+
sort,
|
|
1069
|
+
feed_id: this.id,
|
|
1070
|
+
feed_group_id: this.group,
|
|
1071
|
+
next: currentNextCursor,
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
this.state.next((currentState) => ({
|
|
1075
|
+
...currentState,
|
|
1076
|
+
members: currentState.members
|
|
1077
|
+
? currentState.members.concat(members)
|
|
1078
|
+
: members,
|
|
1079
|
+
member_pagination: {
|
|
1080
|
+
...currentState.member_pagination,
|
|
1081
|
+
next: newNextCursor,
|
|
1082
|
+
// set sort if not defined yet
|
|
1083
|
+
sort: currentState.member_pagination?.sort ?? request.sort,
|
|
1084
|
+
},
|
|
1085
|
+
}));
|
|
1086
|
+
} catch (e) {
|
|
1087
|
+
error = e;
|
|
1088
|
+
} finally {
|
|
1089
|
+
this.state.next((currentState) => ({
|
|
1090
|
+
...currentState,
|
|
1091
|
+
member_pagination: {
|
|
1092
|
+
...currentState.member_pagination,
|
|
1093
|
+
loading_next_page: false,
|
|
1094
|
+
},
|
|
1095
|
+
}));
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if (error) {
|
|
1099
|
+
throw error;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
986
1103
|
/**
|
|
987
1104
|
* Method which queries followers of this feed (feeds which target this feed).
|
|
988
1105
|
*
|
package/src/FeedsClient.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
UserRequest,
|
|
13
13
|
WSEvent,
|
|
14
14
|
} from './gen/models';
|
|
15
|
-
import { FeedsEvent, TokenOrProvider } from './types';
|
|
15
|
+
import { FeedsEvent, StreamFile, TokenOrProvider } from './types';
|
|
16
16
|
import { StateStore } from './common/StateStore';
|
|
17
17
|
import { TokenManager } from './common/TokenManager';
|
|
18
18
|
import { ConnectionIdManager } from './common/ConnectionIdManager';
|
|
@@ -22,6 +22,7 @@ import { ApiClient } from './common/ApiClient';
|
|
|
22
22
|
import {
|
|
23
23
|
addConnectionEventListeners,
|
|
24
24
|
removeConnectionEventListeners,
|
|
25
|
+
streamDevToken,
|
|
25
26
|
} from './common/utils';
|
|
26
27
|
import { decodeWSEvent } from './gen/model-decoders/event-decoder-mapping';
|
|
27
28
|
import { Feed } from './Feed';
|
|
@@ -34,8 +35,8 @@ import { ModerationClient } from './ModerationClient';
|
|
|
34
35
|
import { StreamPoll } from './common/Poll';
|
|
35
36
|
|
|
36
37
|
export type FeedsClientState = {
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
connected_user: OwnUser | undefined;
|
|
39
|
+
is_ws_connection_healthy: boolean;
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
type FID = string;
|
|
@@ -69,8 +70,8 @@ export class FeedsClient extends FeedsApi {
|
|
|
69
70
|
);
|
|
70
71
|
super(apiClient);
|
|
71
72
|
this.state = new StateStore<FeedsClientState>({
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
connected_user: undefined,
|
|
74
|
+
is_ws_connection_healthy: false,
|
|
74
75
|
});
|
|
75
76
|
this.moderation = new ModerationClient(apiClient);
|
|
76
77
|
this.tokenManager = tokenManager;
|
|
@@ -85,7 +86,7 @@ export class FeedsClient extends FeedsApi {
|
|
|
85
86
|
switch (event.type) {
|
|
86
87
|
case 'connection.changed': {
|
|
87
88
|
const { online } = event;
|
|
88
|
-
this.state.partialNext({
|
|
89
|
+
this.state.partialNext({ is_ws_connection_healthy: online });
|
|
89
90
|
|
|
90
91
|
if (online) {
|
|
91
92
|
this.healthyConnectionChangedEventCount++;
|
|
@@ -210,7 +211,7 @@ export class FeedsClient extends FeedsApi {
|
|
|
210
211
|
|
|
211
212
|
connectUser = async (user: UserRequest, tokenProvider: TokenOrProvider) => {
|
|
212
213
|
if (
|
|
213
|
-
this.state.getLatestValue().
|
|
214
|
+
this.state.getLatestValue().connected_user !== undefined ||
|
|
214
215
|
this.wsConnection
|
|
215
216
|
) {
|
|
216
217
|
throw new Error(`Can't connect a new user, call "disconnectUser" first`);
|
|
@@ -234,8 +235,8 @@ export class FeedsClient extends FeedsApi {
|
|
|
234
235
|
);
|
|
235
236
|
const connectedEvent = await this.wsConnection.connect();
|
|
236
237
|
this.state.partialNext({
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
connected_user: connectedEvent?.me,
|
|
239
|
+
is_ws_connection_healthy: !!this.wsConnection?.isHealthy,
|
|
239
240
|
});
|
|
240
241
|
} catch (err) {
|
|
241
242
|
await this.disconnectUser();
|
|
@@ -243,6 +244,10 @@ export class FeedsClient extends FeedsApi {
|
|
|
243
244
|
}
|
|
244
245
|
};
|
|
245
246
|
|
|
247
|
+
devToken = (userId: string) => {
|
|
248
|
+
return streamDevToken(userId);
|
|
249
|
+
};
|
|
250
|
+
|
|
246
251
|
closePoll = async (request: {
|
|
247
252
|
poll_id: string;
|
|
248
253
|
}): Promise<StreamResponse<PollResponse>> => {
|
|
@@ -255,7 +260,9 @@ export class FeedsClient extends FeedsApi {
|
|
|
255
260
|
};
|
|
256
261
|
|
|
257
262
|
// @ts-expect-error API spec says file should be a string
|
|
258
|
-
uploadFile = (
|
|
263
|
+
uploadFile = (
|
|
264
|
+
request: Omit<FileUploadRequest, 'file'> & { file: StreamFile },
|
|
265
|
+
) => {
|
|
259
266
|
return super.uploadFile({
|
|
260
267
|
// @ts-expect-error API spec says file should be a string
|
|
261
268
|
file: request.file,
|
|
@@ -264,7 +271,7 @@ export class FeedsClient extends FeedsApi {
|
|
|
264
271
|
|
|
265
272
|
// @ts-expect-error API spec says file should be a string
|
|
266
273
|
uploadImage = (
|
|
267
|
-
request: Omit<ImageUploadRequest, 'file'> & { file:
|
|
274
|
+
request: Omit<ImageUploadRequest, 'file'> & { file: StreamFile },
|
|
268
275
|
) => {
|
|
269
276
|
return super.uploadImage({
|
|
270
277
|
// @ts-expect-error API spec says file should be a string
|
|
@@ -311,7 +318,10 @@ export class FeedsClient extends FeedsApi {
|
|
|
311
318
|
|
|
312
319
|
this.connectionIdManager.reset();
|
|
313
320
|
this.tokenManager.reset();
|
|
314
|
-
this.state.partialNext({
|
|
321
|
+
this.state.partialNext({
|
|
322
|
+
connected_user: undefined,
|
|
323
|
+
is_ws_connection_healthy: false,
|
|
324
|
+
});
|
|
315
325
|
};
|
|
316
326
|
|
|
317
327
|
on = this.eventDispatcher.on;
|