@stream-io/feeds-client 0.1.8 → 0.1.9
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/util/index.ts +1 -0
- package/CHANGELOG.md +15 -0
- package/dist/@react-bindings/hooks/util/index.d.ts +1 -0
- package/dist/@react-bindings/hooks/util/useBookmarkActions.d.ts +13 -0
- package/dist/@react-bindings/hooks/util/useReactionActions.d.ts +1 -1
- package/dist/index-react-bindings.browser.cjs +346 -140
- package/dist/index-react-bindings.browser.cjs.map +1 -1
- package/dist/index-react-bindings.browser.js +346 -141
- package/dist/index-react-bindings.browser.js.map +1 -1
- package/dist/index-react-bindings.node.cjs +346 -140
- package/dist/index-react-bindings.node.cjs.map +1 -1
- package/dist/index-react-bindings.node.js +346 -141
- package/dist/index-react-bindings.node.js.map +1 -1
- package/dist/index.browser.cjs +320 -139
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +320 -140
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +320 -139
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +320 -140
- package/dist/index.node.js.map +1 -1
- package/dist/src/Feed.d.ts +40 -9
- package/dist/src/FeedsClient.d.ts +8 -1
- package/dist/src/gen-imports.d.ts +1 -1
- package/dist/src/state-updates/follow-utils.d.ts +19 -0
- package/dist/src/state-updates/state-update-queue.d.ts +15 -0
- package/dist/src/utils.d.ts +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Feed.ts +226 -192
- package/src/FeedsClient.ts +75 -3
- package/src/gen-imports.ts +1 -1
- package/src/state-updates/activity-reaction-utils.test.ts +1 -0
- package/src/state-updates/activity-utils.test.ts +1 -0
- package/src/state-updates/follow-utils.test.ts +552 -0
- package/src/state-updates/follow-utils.ts +126 -0
- package/src/state-updates/state-update-queue.test.ts +53 -0
- package/src/state-updates/state-update-queue.ts +35 -0
- package/src/utils.test.ts +175 -0
- package/src/utils.ts +20 -0
|
@@ -4048,6 +4048,89 @@ const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
|
|
|
4048
4048
|
return updateActivityInActivities(updatedActivity, activities);
|
|
4049
4049
|
};
|
|
4050
4050
|
|
|
4051
|
+
const isFeedResponse = (follow) => {
|
|
4052
|
+
return 'created_by' in follow;
|
|
4053
|
+
};
|
|
4054
|
+
const handleFollowCreated = (follow, currentState, currentFeedId, connectedUserId) => {
|
|
4055
|
+
// filter non-accepted follows (the way getOrCreate does by default)
|
|
4056
|
+
if (follow.status !== 'accepted') {
|
|
4057
|
+
return { changed: false, data: currentState };
|
|
4058
|
+
}
|
|
4059
|
+
let newState = { ...currentState };
|
|
4060
|
+
// this feed followed someone
|
|
4061
|
+
if (follow.source_feed.fid === currentFeedId) {
|
|
4062
|
+
newState = {
|
|
4063
|
+
...newState,
|
|
4064
|
+
// Update FeedResponse fields, that has the new follower/following count
|
|
4065
|
+
...follow.source_feed,
|
|
4066
|
+
};
|
|
4067
|
+
// Only update if following array already exists
|
|
4068
|
+
if (currentState.following !== undefined) {
|
|
4069
|
+
newState.following = [follow, ...currentState.following];
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
else if (
|
|
4073
|
+
// someone followed this feed
|
|
4074
|
+
follow.target_feed.fid === currentFeedId) {
|
|
4075
|
+
const source = follow.source_feed;
|
|
4076
|
+
newState = {
|
|
4077
|
+
...newState,
|
|
4078
|
+
// Update FeedResponse fields, that has the new follower/following count
|
|
4079
|
+
...follow.target_feed,
|
|
4080
|
+
};
|
|
4081
|
+
if (source.created_by.id === connectedUserId) {
|
|
4082
|
+
newState.own_follows = currentState.own_follows
|
|
4083
|
+
? currentState.own_follows.concat(follow)
|
|
4084
|
+
: [follow];
|
|
4085
|
+
}
|
|
4086
|
+
// Only update if followers array already exists
|
|
4087
|
+
if (currentState.followers !== undefined) {
|
|
4088
|
+
newState.followers = [follow, ...currentState.followers];
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
return { changed: true, data: newState };
|
|
4092
|
+
};
|
|
4093
|
+
const handleFollowDeleted = (follow, currentState, currentFeedId, connectedUserId) => {
|
|
4094
|
+
let newState = { ...currentState };
|
|
4095
|
+
// this feed unfollowed someone
|
|
4096
|
+
if (follow.source_feed.fid === currentFeedId) {
|
|
4097
|
+
newState = {
|
|
4098
|
+
...newState,
|
|
4099
|
+
// Update FeedResponse fields, that has the new follower/following count
|
|
4100
|
+
...follow.source_feed,
|
|
4101
|
+
};
|
|
4102
|
+
// Only update if following array already exists
|
|
4103
|
+
if (currentState.following !== undefined) {
|
|
4104
|
+
newState.following = currentState.following.filter((followItem) => followItem.target_feed.fid !== follow.target_feed.fid);
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
else if (
|
|
4108
|
+
// someone unfollowed this feed
|
|
4109
|
+
follow.target_feed.fid === currentFeedId) {
|
|
4110
|
+
const source = follow.source_feed;
|
|
4111
|
+
newState = {
|
|
4112
|
+
...newState,
|
|
4113
|
+
// Update FeedResponse fields, that has the new follower/following count
|
|
4114
|
+
...follow.target_feed,
|
|
4115
|
+
};
|
|
4116
|
+
if (isFeedResponse(source) &&
|
|
4117
|
+
source.created_by.id === connectedUserId &&
|
|
4118
|
+
currentState.own_follows !== undefined) {
|
|
4119
|
+
newState.own_follows = currentState.own_follows.filter((followItem) => followItem.source_feed.fid !== follow.source_feed.fid);
|
|
4120
|
+
}
|
|
4121
|
+
// Only update if followers array already exists
|
|
4122
|
+
if (currentState.followers !== undefined) {
|
|
4123
|
+
newState.followers = currentState.followers.filter((followItem) => followItem.source_feed.fid !== follow.source_feed.fid);
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
return { changed: true, data: newState };
|
|
4127
|
+
};
|
|
4128
|
+
const handleFollowUpdated = (currentState) => {
|
|
4129
|
+
// For now, we'll treat follow updates as no-ops since the current implementation does
|
|
4130
|
+
// This can be enhanced later if needed
|
|
4131
|
+
return { changed: false, data: currentState };
|
|
4132
|
+
};
|
|
4133
|
+
|
|
4051
4134
|
const checkHasAnotherPage = (v, cursor) => (typeof v === 'undefined' && typeof cursor === 'undefined') ||
|
|
4052
4135
|
typeof cursor === 'string';
|
|
4053
4136
|
const isCommentResponse = (entity) => {
|
|
@@ -4056,11 +4139,41 @@ const isCommentResponse = (entity) => {
|
|
|
4056
4139
|
const Constants = {
|
|
4057
4140
|
DEFAULT_COMMENT_PAGINATION: 'first',
|
|
4058
4141
|
};
|
|
4142
|
+
const uniqueArrayMerge = (existingArray, arrayToMerge, getKey) => {
|
|
4143
|
+
const existing = new Set();
|
|
4144
|
+
existingArray.forEach((value) => {
|
|
4145
|
+
const key = getKey(value);
|
|
4146
|
+
existing.add(key);
|
|
4147
|
+
});
|
|
4148
|
+
const filteredArrayToMerge = arrayToMerge.filter((value) => {
|
|
4149
|
+
const key = getKey(value);
|
|
4150
|
+
return !existing.has(key);
|
|
4151
|
+
});
|
|
4152
|
+
return existingArray.concat(filteredArrayToMerge);
|
|
4153
|
+
};
|
|
4154
|
+
|
|
4155
|
+
const shouldUpdateState = ({ stateUpdateId, stateUpdateQueue, watch, }) => {
|
|
4156
|
+
if (!watch) {
|
|
4157
|
+
return true;
|
|
4158
|
+
}
|
|
4159
|
+
if (watch && stateUpdateQueue.has(stateUpdateId)) {
|
|
4160
|
+
stateUpdateQueue.delete(stateUpdateId);
|
|
4161
|
+
return false;
|
|
4162
|
+
}
|
|
4163
|
+
stateUpdateQueue.add(stateUpdateId);
|
|
4164
|
+
return true;
|
|
4165
|
+
};
|
|
4166
|
+
const getStateUpdateQueueIdForFollow = (follow) => {
|
|
4167
|
+
return `follow${follow.source_feed.fid}-${follow.target_feed.fid}`;
|
|
4168
|
+
};
|
|
4169
|
+
const getStateUpdateQueueIdForUnfollow = (follow) => {
|
|
4170
|
+
return `unfollow${follow.source_feed.fid}-${follow.target_feed.fid}`;
|
|
4171
|
+
};
|
|
4059
4172
|
|
|
4060
4173
|
class Feed extends FeedApi {
|
|
4061
|
-
constructor(client, groupId, id, data) {
|
|
4062
|
-
// Need this ugly cast because fileUpload endpoints :(
|
|
4174
|
+
constructor(client, groupId, id, data, watch = false) {
|
|
4063
4175
|
super(client, groupId, id);
|
|
4176
|
+
this.stateUpdateQueue = new Set();
|
|
4064
4177
|
this.eventHandlers = {
|
|
4065
4178
|
'feeds.activity.added': (event) => {
|
|
4066
4179
|
const currentActivities = this.currentState.activities;
|
|
@@ -4206,74 +4319,14 @@ class Feed extends FeedApi {
|
|
|
4206
4319
|
'feeds.feed_group.changed': Feed.noop,
|
|
4207
4320
|
'feeds.feed_group.deleted': Feed.noop,
|
|
4208
4321
|
'feeds.follow.created': (event) => {
|
|
4209
|
-
|
|
4210
|
-
if (event.follow.status !== 'accepted')
|
|
4211
|
-
return;
|
|
4212
|
-
// this feed followed someone
|
|
4213
|
-
if (event.follow.source_feed.fid === this.fid) {
|
|
4214
|
-
this.state.next((currentState) => {
|
|
4215
|
-
const newState = {
|
|
4216
|
-
...currentState,
|
|
4217
|
-
...event.follow.source_feed,
|
|
4218
|
-
};
|
|
4219
|
-
if (!checkHasAnotherPage(currentState.following, currentState.following_pagination?.next)) {
|
|
4220
|
-
// TODO: respect sort
|
|
4221
|
-
newState.following = currentState.following
|
|
4222
|
-
? currentState.following.concat(event.follow)
|
|
4223
|
-
: [event.follow];
|
|
4224
|
-
}
|
|
4225
|
-
return newState;
|
|
4226
|
-
});
|
|
4227
|
-
}
|
|
4228
|
-
else if (
|
|
4229
|
-
// someone followed this feed
|
|
4230
|
-
event.follow.target_feed.fid === this.fid) {
|
|
4231
|
-
const source = event.follow.source_feed;
|
|
4232
|
-
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
4233
|
-
this.state.next((currentState) => {
|
|
4234
|
-
const newState = { ...currentState, ...event.follow.target_feed };
|
|
4235
|
-
if (source.created_by.id === connectedUser?.id) {
|
|
4236
|
-
newState.own_follows = currentState.own_follows
|
|
4237
|
-
? currentState.own_follows.concat(event.follow)
|
|
4238
|
-
: [event.follow];
|
|
4239
|
-
}
|
|
4240
|
-
if (!checkHasAnotherPage(currentState.followers, currentState.followers_pagination?.next)) {
|
|
4241
|
-
// TODO: respect sort
|
|
4242
|
-
newState.followers = currentState.followers
|
|
4243
|
-
? currentState.followers.concat(event.follow)
|
|
4244
|
-
: [event.follow];
|
|
4245
|
-
}
|
|
4246
|
-
return newState;
|
|
4247
|
-
});
|
|
4248
|
-
}
|
|
4322
|
+
this.handleFollowCreated(event.follow);
|
|
4249
4323
|
},
|
|
4250
4324
|
'feeds.follow.deleted': (event) => {
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
...currentState,
|
|
4256
|
-
...event.follow.source_feed,
|
|
4257
|
-
following: currentState.following?.filter((follow) => follow.target_feed.fid !== event.follow.target_feed.fid),
|
|
4258
|
-
};
|
|
4259
|
-
});
|
|
4260
|
-
}
|
|
4261
|
-
else if (
|
|
4262
|
-
// someone unfollowed this feed
|
|
4263
|
-
event.follow.target_feed.fid === this.fid) {
|
|
4264
|
-
const source = event.follow.source_feed;
|
|
4265
|
-
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
4266
|
-
this.state.next((currentState) => {
|
|
4267
|
-
const newState = { ...currentState, ...event.follow.target_feed };
|
|
4268
|
-
if (source.created_by.id === connectedUser?.id) {
|
|
4269
|
-
newState.own_follows = currentState.own_follows?.filter((follow) => follow.source_feed.fid !== event.follow.source_feed.fid);
|
|
4270
|
-
}
|
|
4271
|
-
newState.followers = currentState.followers?.filter((follow) => follow.source_feed.fid !== event.follow.source_feed.fid);
|
|
4272
|
-
return newState;
|
|
4273
|
-
});
|
|
4274
|
-
}
|
|
4325
|
+
this.handleFollowDeleted(event.follow);
|
|
4326
|
+
},
|
|
4327
|
+
'feeds.follow.updated': (_event) => {
|
|
4328
|
+
handleFollowUpdated(this.currentState);
|
|
4275
4329
|
},
|
|
4276
|
-
'feeds.follow.updated': Feed.noop,
|
|
4277
4330
|
'feeds.comment.reaction.added': this.handleCommentReactionEvent.bind(this),
|
|
4278
4331
|
'feeds.comment.reaction.deleted': this.handleCommentReactionEvent.bind(this),
|
|
4279
4332
|
'feeds.comment.reaction.updated': Feed.noop,
|
|
@@ -4281,13 +4334,11 @@ class Feed extends FeedApi {
|
|
|
4281
4334
|
const { connected_user: connectedUser } = this.client.state.getLatestValue();
|
|
4282
4335
|
this.state.next((currentState) => {
|
|
4283
4336
|
let newState;
|
|
4284
|
-
if (
|
|
4337
|
+
if (typeof currentState.members !== 'undefined') {
|
|
4285
4338
|
newState ?? (newState = {
|
|
4286
4339
|
...currentState,
|
|
4287
4340
|
});
|
|
4288
|
-
newState.members =
|
|
4289
|
-
event.member,
|
|
4290
|
-
];
|
|
4341
|
+
newState.members = [event.member, ...currentState.members];
|
|
4291
4342
|
}
|
|
4292
4343
|
if (connectedUser?.id === event.member.user.id) {
|
|
4293
4344
|
newState ?? (newState = {
|
|
@@ -4370,6 +4421,7 @@ class Feed extends FeedApi {
|
|
|
4370
4421
|
is_loading: false,
|
|
4371
4422
|
is_loading_activities: false,
|
|
4372
4423
|
comments_by_entity_id: {},
|
|
4424
|
+
watch,
|
|
4373
4425
|
});
|
|
4374
4426
|
this.client = client;
|
|
4375
4427
|
}
|
|
@@ -4449,6 +4501,8 @@ class Feed extends FeedApi {
|
|
|
4449
4501
|
}
|
|
4450
4502
|
}
|
|
4451
4503
|
else {
|
|
4504
|
+
// Empty queue when reinitializing the state
|
|
4505
|
+
this.stateUpdateQueue.clear();
|
|
4452
4506
|
const responseCopy = {
|
|
4453
4507
|
...response,
|
|
4454
4508
|
...response.feed,
|
|
@@ -4467,7 +4521,11 @@ class Feed extends FeedApi {
|
|
|
4467
4521
|
if (!request?.following_pagination?.limit) {
|
|
4468
4522
|
delete nextState.following;
|
|
4469
4523
|
}
|
|
4524
|
+
if (response.members.length === 0 && response.feed.member_count > 0) {
|
|
4525
|
+
delete nextState.members;
|
|
4526
|
+
}
|
|
4470
4527
|
nextState.last_get_or_create_request_config = request;
|
|
4528
|
+
nextState.watch = request?.watch ? request.watch : currentState.watch;
|
|
4471
4529
|
return nextState;
|
|
4472
4530
|
});
|
|
4473
4531
|
}
|
|
@@ -4481,6 +4539,56 @@ class Feed extends FeedApi {
|
|
|
4481
4539
|
});
|
|
4482
4540
|
}
|
|
4483
4541
|
}
|
|
4542
|
+
/**
|
|
4543
|
+
* @internal
|
|
4544
|
+
*/
|
|
4545
|
+
handleFollowCreated(follow) {
|
|
4546
|
+
if (!shouldUpdateState({
|
|
4547
|
+
stateUpdateId: getStateUpdateQueueIdForFollow(follow),
|
|
4548
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
4549
|
+
watch: this.currentState.watch,
|
|
4550
|
+
})) {
|
|
4551
|
+
return;
|
|
4552
|
+
}
|
|
4553
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
4554
|
+
const result = handleFollowCreated(follow, this.currentState, this.fid, connectedUser?.id);
|
|
4555
|
+
if (result.changed) {
|
|
4556
|
+
this.state.next(result.data);
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
/**
|
|
4560
|
+
* @internal
|
|
4561
|
+
*/
|
|
4562
|
+
handleFollowDeleted(follow) {
|
|
4563
|
+
if (!shouldUpdateState({
|
|
4564
|
+
stateUpdateId: getStateUpdateQueueIdForUnfollow(follow),
|
|
4565
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
4566
|
+
watch: this.currentState.watch,
|
|
4567
|
+
})) {
|
|
4568
|
+
return;
|
|
4569
|
+
}
|
|
4570
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
4571
|
+
const result = handleFollowDeleted(follow, this.currentState, this.fid, connectedUser?.id);
|
|
4572
|
+
{
|
|
4573
|
+
this.state.next(result.data);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
/**
|
|
4577
|
+
* @internal
|
|
4578
|
+
*/
|
|
4579
|
+
handleWatchStopped() {
|
|
4580
|
+
this.state.partialNext({
|
|
4581
|
+
watch: false,
|
|
4582
|
+
});
|
|
4583
|
+
}
|
|
4584
|
+
/**
|
|
4585
|
+
* @internal
|
|
4586
|
+
*/
|
|
4587
|
+
handleWatchStarted() {
|
|
4588
|
+
this.state.partialNext({
|
|
4589
|
+
watch: true,
|
|
4590
|
+
});
|
|
4591
|
+
}
|
|
4484
4592
|
handleBookmarkAdded(event) {
|
|
4485
4593
|
const currentActivities = this.currentState.activities;
|
|
4486
4594
|
const { connected_user: connectedUser } = this.client.state.getLatestValue();
|
|
@@ -4525,70 +4633,85 @@ class Feed extends FeedApi {
|
|
|
4525
4633
|
}
|
|
4526
4634
|
return commentIndex;
|
|
4527
4635
|
}
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4636
|
+
/**
|
|
4637
|
+
* Load child comments of entity (activity or comment) into the state, if the target entity is comment,
|
|
4638
|
+
* `entityParentId` should be provided (`CommentResponse.parent_id ?? CommentResponse.object_id`).
|
|
4639
|
+
*/
|
|
4640
|
+
loadCommentsIntoState(data) {
|
|
4641
|
+
// add initial (top level) object for processing
|
|
4642
|
+
const traverseArray = [
|
|
4643
|
+
{
|
|
4644
|
+
entityId: data.entityId,
|
|
4645
|
+
entityParentId: data.entityParentId,
|
|
4646
|
+
comments: data.comments,
|
|
4647
|
+
next: data.next,
|
|
4648
|
+
},
|
|
4649
|
+
];
|
|
4541
4650
|
this.state.next((currentState) => {
|
|
4542
|
-
const
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4651
|
+
const newCommentsByEntityId = {
|
|
4652
|
+
...currentState.comments_by_entity_id,
|
|
4653
|
+
};
|
|
4654
|
+
while (traverseArray.length) {
|
|
4655
|
+
const item = traverseArray.pop();
|
|
4656
|
+
const entityId = item.entityId;
|
|
4657
|
+
// go over entity comments and generate new objects
|
|
4658
|
+
// for further processing if there are any replies
|
|
4659
|
+
item.comments.forEach((comment) => {
|
|
4660
|
+
if (!comment.replies?.length)
|
|
4661
|
+
return;
|
|
4662
|
+
traverseArray.push({
|
|
4663
|
+
entityId: comment.id,
|
|
4664
|
+
entityParentId: entityId,
|
|
4665
|
+
comments: comment.replies,
|
|
4666
|
+
next: comment.meta?.next_cursor,
|
|
4667
|
+
});
|
|
4668
|
+
});
|
|
4669
|
+
// omit replies & meta from the comments (transform ThreadedCommentResponse to CommentResponse)
|
|
4670
|
+
// this is somehow faster than copying the whole
|
|
4671
|
+
// object and deleting the desired properties
|
|
4672
|
+
const newComments = item.comments.map(({ replies: _r, meta: _m, ...restOfTheCommentResponse }) => restOfTheCommentResponse);
|
|
4673
|
+
newCommentsByEntityId[entityId] = {
|
|
4674
|
+
...newCommentsByEntityId[entityId],
|
|
4675
|
+
entity_parent_id: item.entityParentId,
|
|
4676
|
+
pagination: {
|
|
4677
|
+
...newCommentsByEntityId[entityId]?.pagination,
|
|
4678
|
+
next: item.next,
|
|
4679
|
+
sort: data.sort,
|
|
4680
|
+
},
|
|
4681
|
+
comments: newCommentsByEntityId[entityId]?.comments
|
|
4682
|
+
? newCommentsByEntityId[entityId].comments?.concat(newComments)
|
|
4683
|
+
: newComments,
|
|
4684
|
+
};
|
|
4685
|
+
}
|
|
4547
4686
|
return {
|
|
4548
4687
|
...currentState,
|
|
4549
|
-
|
|
4688
|
+
comments_by_entity_id: newCommentsByEntityId,
|
|
4550
4689
|
};
|
|
4551
4690
|
});
|
|
4552
4691
|
}
|
|
4553
|
-
async loadNextPageComments({
|
|
4692
|
+
async loadNextPageComments({ entityId, base, sort, entityParentId, }) {
|
|
4554
4693
|
let error;
|
|
4555
4694
|
try {
|
|
4556
4695
|
this.state.next((currentState) => ({
|
|
4557
4696
|
...currentState,
|
|
4558
4697
|
comments_by_entity_id: {
|
|
4559
4698
|
...currentState.comments_by_entity_id,
|
|
4560
|
-
[
|
|
4561
|
-
...currentState.comments_by_entity_id[
|
|
4699
|
+
[entityId]: {
|
|
4700
|
+
...currentState.comments_by_entity_id[entityId],
|
|
4562
4701
|
pagination: {
|
|
4563
|
-
...currentState.comments_by_entity_id[
|
|
4702
|
+
...currentState.comments_by_entity_id[entityId]?.pagination,
|
|
4564
4703
|
loading_next_page: true,
|
|
4565
4704
|
},
|
|
4566
4705
|
},
|
|
4567
4706
|
},
|
|
4568
4707
|
}));
|
|
4569
|
-
const { next
|
|
4570
|
-
this.
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
newPagination.sort = sort;
|
|
4577
|
-
}
|
|
4578
|
-
return {
|
|
4579
|
-
...currentState,
|
|
4580
|
-
comments_by_entity_id: {
|
|
4581
|
-
...currentState.comments_by_entity_id,
|
|
4582
|
-
[forId]: {
|
|
4583
|
-
...currentState.comments_by_entity_id[forId],
|
|
4584
|
-
parent_id: parentId,
|
|
4585
|
-
pagination: newPagination,
|
|
4586
|
-
comments: currentState.comments_by_entity_id[forId]?.comments
|
|
4587
|
-
? currentState.comments_by_entity_id[forId].comments?.concat(comments)
|
|
4588
|
-
: comments,
|
|
4589
|
-
},
|
|
4590
|
-
},
|
|
4591
|
-
};
|
|
4708
|
+
const { next, comments } = await base();
|
|
4709
|
+
this.loadCommentsIntoState({
|
|
4710
|
+
entityId,
|
|
4711
|
+
comments,
|
|
4712
|
+
entityParentId,
|
|
4713
|
+
next,
|
|
4714
|
+
sort,
|
|
4592
4715
|
});
|
|
4593
4716
|
}
|
|
4594
4717
|
catch (e) {
|
|
@@ -4599,10 +4722,10 @@ class Feed extends FeedApi {
|
|
|
4599
4722
|
...currentState,
|
|
4600
4723
|
comments_by_entity_id: {
|
|
4601
4724
|
...currentState.comments_by_entity_id,
|
|
4602
|
-
[
|
|
4603
|
-
...currentState.comments_by_entity_id[
|
|
4725
|
+
[entityId]: {
|
|
4726
|
+
...currentState.comments_by_entity_id[entityId],
|
|
4604
4727
|
pagination: {
|
|
4605
|
-
...currentState.comments_by_entity_id[
|
|
4728
|
+
...currentState.comments_by_entity_id[entityId]?.pagination,
|
|
4606
4729
|
loading_next_page: false,
|
|
4607
4730
|
},
|
|
4608
4731
|
},
|
|
@@ -4625,7 +4748,7 @@ class Feed extends FeedApi {
|
|
|
4625
4748
|
return;
|
|
4626
4749
|
}
|
|
4627
4750
|
await this.loadNextPageComments({
|
|
4628
|
-
|
|
4751
|
+
entityId: activity.id,
|
|
4629
4752
|
base: () => this.client.getComments({
|
|
4630
4753
|
...request,
|
|
4631
4754
|
sort,
|
|
@@ -4648,7 +4771,7 @@ class Feed extends FeedApi {
|
|
|
4648
4771
|
return;
|
|
4649
4772
|
}
|
|
4650
4773
|
await this.loadNextPageComments({
|
|
4651
|
-
|
|
4774
|
+
entityId: comment.id,
|
|
4652
4775
|
base: () => this.client.getCommentReplies({
|
|
4653
4776
|
...request,
|
|
4654
4777
|
comment_id: comment.id,
|
|
@@ -4658,7 +4781,7 @@ class Feed extends FeedApi {
|
|
|
4658
4781
|
Constants.DEFAULT_COMMENT_PAGINATION,
|
|
4659
4782
|
next: currentNextCursor,
|
|
4660
4783
|
}),
|
|
4661
|
-
|
|
4784
|
+
entityParentId: comment.parent_id ?? comment.object_id,
|
|
4662
4785
|
sort,
|
|
4663
4786
|
});
|
|
4664
4787
|
}
|
|
@@ -4688,17 +4811,19 @@ class Feed extends FeedApi {
|
|
|
4688
4811
|
next: currentNextCursor,
|
|
4689
4812
|
sort,
|
|
4690
4813
|
});
|
|
4691
|
-
this.state.next((currentState) =>
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4814
|
+
this.state.next((currentState) => {
|
|
4815
|
+
return {
|
|
4816
|
+
...currentState,
|
|
4817
|
+
[type]: currentState[type] === undefined
|
|
4818
|
+
? follows
|
|
4819
|
+
: uniqueArrayMerge(currentState[type], follows, (follow) => `${follow.source_feed.fid}-${follow.target_feed.fid}`),
|
|
4820
|
+
[paginationKey]: {
|
|
4821
|
+
...currentState[paginationKey],
|
|
4822
|
+
next: newNextCursor,
|
|
4823
|
+
sort,
|
|
4824
|
+
},
|
|
4825
|
+
};
|
|
4826
|
+
});
|
|
4702
4827
|
}
|
|
4703
4828
|
catch (e) {
|
|
4704
4829
|
error = e;
|
|
@@ -4751,7 +4876,7 @@ class Feed extends FeedApi {
|
|
|
4751
4876
|
this.state.next((currentState) => ({
|
|
4752
4877
|
...currentState,
|
|
4753
4878
|
members: currentState.members
|
|
4754
|
-
? currentState.members
|
|
4879
|
+
? uniqueArrayMerge(currentState.members, members, ({ user }) => user.id)
|
|
4755
4880
|
: members,
|
|
4756
4881
|
member_pagination: {
|
|
4757
4882
|
...currentState.member_pagination,
|
|
@@ -5338,13 +5463,17 @@ class FeedsClient extends FeedsApi {
|
|
|
5338
5463
|
};
|
|
5339
5464
|
this.eventDispatcher.dispatch(networkEvent);
|
|
5340
5465
|
};
|
|
5341
|
-
this.getOrCreateActiveFeed = (group, id, data) => {
|
|
5466
|
+
this.getOrCreateActiveFeed = (group, id, data, watch) => {
|
|
5342
5467
|
const fid = `${group}:${id}`;
|
|
5343
5468
|
if (this.activeFeeds[fid]) {
|
|
5344
|
-
|
|
5469
|
+
const feed = this.activeFeeds[fid];
|
|
5470
|
+
if (watch && !feed.currentState.watch) {
|
|
5471
|
+
feed.handleWatchStarted();
|
|
5472
|
+
}
|
|
5473
|
+
return feed;
|
|
5345
5474
|
}
|
|
5346
5475
|
else {
|
|
5347
|
-
const feed = new Feed(this, group, id, data);
|
|
5476
|
+
const feed = new Feed(this, group, id, data, watch);
|
|
5348
5477
|
this.activeFeeds[fid] = feed;
|
|
5349
5478
|
return feed;
|
|
5350
5479
|
}
|
|
@@ -5373,6 +5502,11 @@ class FeedsClient extends FeedsApi {
|
|
|
5373
5502
|
}
|
|
5374
5503
|
}
|
|
5375
5504
|
}
|
|
5505
|
+
else {
|
|
5506
|
+
for (const activeFeed of Object.values(this.activeFeeds)) {
|
|
5507
|
+
activeFeed.handleWatchStopped();
|
|
5508
|
+
}
|
|
5509
|
+
}
|
|
5376
5510
|
break;
|
|
5377
5511
|
}
|
|
5378
5512
|
case 'feeds.feed.created': {
|
|
@@ -5476,7 +5610,7 @@ class FeedsClient extends FeedsApi {
|
|
|
5476
5610
|
}
|
|
5477
5611
|
async queryFeeds(request) {
|
|
5478
5612
|
const response = await this.feedsQueryFeeds(request);
|
|
5479
|
-
const feeds = response.feeds.map((f) => this.getOrCreateActiveFeed(f.group_id, f.id, f));
|
|
5613
|
+
const feeds = response.feeds.map((f) => this.getOrCreateActiveFeed(f.group_id, f.id, f, request?.watch));
|
|
5480
5614
|
return {
|
|
5481
5615
|
feeds,
|
|
5482
5616
|
next: response.next,
|
|
@@ -5485,6 +5619,52 @@ class FeedsClient extends FeedsApi {
|
|
|
5485
5619
|
duration: response.duration,
|
|
5486
5620
|
};
|
|
5487
5621
|
}
|
|
5622
|
+
// For follow API endpoints we update the state after HTTP response to allow queryFeeds with watch: false
|
|
5623
|
+
async follow(request) {
|
|
5624
|
+
const response = await super.follow(request);
|
|
5625
|
+
[response.follow.source_feed.fid, response.follow.target_feed.fid].forEach((fid) => {
|
|
5626
|
+
const feed = this.activeFeeds[fid];
|
|
5627
|
+
if (feed) {
|
|
5628
|
+
feed.handleFollowCreated(response.follow);
|
|
5629
|
+
}
|
|
5630
|
+
});
|
|
5631
|
+
return response;
|
|
5632
|
+
}
|
|
5633
|
+
async followBatch(request) {
|
|
5634
|
+
const response = await super.followBatch(request);
|
|
5635
|
+
response.follows.forEach((follow) => {
|
|
5636
|
+
const feed = this.activeFeeds[follow.source_feed.fid];
|
|
5637
|
+
if (feed) {
|
|
5638
|
+
feed.handleFollowCreated(follow);
|
|
5639
|
+
}
|
|
5640
|
+
});
|
|
5641
|
+
return response;
|
|
5642
|
+
}
|
|
5643
|
+
async unfollow(request) {
|
|
5644
|
+
const response = await super.unfollow(request);
|
|
5645
|
+
[request.source, request.target].forEach((fid) => {
|
|
5646
|
+
const feed = this.activeFeeds[fid];
|
|
5647
|
+
if (feed) {
|
|
5648
|
+
feed.handleFollowDeleted({
|
|
5649
|
+
source_feed: { fid: request.source },
|
|
5650
|
+
target_feed: { fid: request.target },
|
|
5651
|
+
});
|
|
5652
|
+
}
|
|
5653
|
+
});
|
|
5654
|
+
return response;
|
|
5655
|
+
}
|
|
5656
|
+
async stopWatchingFeed(request) {
|
|
5657
|
+
const connectionId = await this.connectionIdManager.getConnectionId();
|
|
5658
|
+
const response = await super.stopWatchingFeed({
|
|
5659
|
+
...request,
|
|
5660
|
+
connection_id: connectionId,
|
|
5661
|
+
});
|
|
5662
|
+
const feed = this.activeFeeds[`${request.feed_group_id}:${request.feed_id}`];
|
|
5663
|
+
if (feed) {
|
|
5664
|
+
feed.handleWatchStopped();
|
|
5665
|
+
}
|
|
5666
|
+
return response;
|
|
5667
|
+
}
|
|
5488
5668
|
findActiveFeedByActivityId(activityId) {
|
|
5489
5669
|
return Object.values(this.activeFeeds).filter((feed) => feed.currentState.activities?.some((activity) => activity.id === activityId));
|
|
5490
5670
|
}
|
|
@@ -5816,7 +5996,7 @@ const selector = ({ own_follows }) => ({
|
|
|
5816
5996
|
* that can then be used on the UI. The entity can be either an ActivityResponse or a CommentResponse
|
|
5817
5997
|
* as the hook determines internally which APIs it is supposed to use, while taking the
|
|
5818
5998
|
* correct ownCapabilities into account.
|
|
5819
|
-
* @param entity - The entity to which we want to
|
|
5999
|
+
* @param entity - The entity to which we want to apply reaction actions, can be either ActivityResponse or CommentResponse.
|
|
5820
6000
|
* @param type - The type of reaction we want to add or remove.
|
|
5821
6001
|
*/
|
|
5822
6002
|
const useReactionActions = ({ entity, type, }) => {
|
|
@@ -5847,6 +6027,31 @@ const useReactionActions = ({ entity, type, }) => {
|
|
|
5847
6027
|
return useMemo(() => ({ addReaction, removeReaction, toggleReaction }), [addReaction, removeReaction, toggleReaction]);
|
|
5848
6028
|
};
|
|
5849
6029
|
|
|
6030
|
+
/**
|
|
6031
|
+
* A utility hook that takes in an entity and creates bookmark actions
|
|
6032
|
+
* that can then be used on the UI. The entity is expected to be an ActivityResponse.
|
|
6033
|
+
* @param entity - The entity to which we want to apply reaction actions, expects an ActivityResponse.
|
|
6034
|
+
*/
|
|
6035
|
+
const useBookmarkActions = ({ entity, }) => {
|
|
6036
|
+
const client = useFeedsClient();
|
|
6037
|
+
const hasOwnBookmark = entity.own_bookmarks?.length > 0;
|
|
6038
|
+
const addBookmark = useStableCallback(async () => {
|
|
6039
|
+
await client?.addBookmark({ activity_id: entity.id });
|
|
6040
|
+
});
|
|
6041
|
+
const removeBookmark = useStableCallback(async () => {
|
|
6042
|
+
await client?.deleteBookmark({ activity_id: entity.id });
|
|
6043
|
+
});
|
|
6044
|
+
const toggleBookmark = useStableCallback(async () => {
|
|
6045
|
+
if (hasOwnBookmark) {
|
|
6046
|
+
await removeBookmark();
|
|
6047
|
+
}
|
|
6048
|
+
else {
|
|
6049
|
+
await addBookmark();
|
|
6050
|
+
}
|
|
6051
|
+
});
|
|
6052
|
+
return useMemo(() => ({ addBookmark, removeBookmark, toggleBookmark }), [addBookmark, removeBookmark, toggleBookmark]);
|
|
6053
|
+
};
|
|
6054
|
+
|
|
5850
6055
|
const StreamFeeds = ({ client, children }) => {
|
|
5851
6056
|
return (jsx(StreamFeedsContext.Provider, { value: client, children: children }));
|
|
5852
6057
|
};
|
|
@@ -5857,5 +6062,5 @@ const StreamFeed = ({ feed, children }) => {
|
|
|
5857
6062
|
};
|
|
5858
6063
|
StreamFeed.displayName = 'StreamFeed';
|
|
5859
6064
|
|
|
5860
|
-
export { StreamFeed, StreamFeedContext, StreamFeeds, StreamFeedsContext, useClientConnectedUser, useComments, useCreateFeedsClient, useFeedActivities, useFeedContext, useFeedMetadata, useFeedsClient, useFollowers, useFollowing, useOwnCapabilities, useOwnFollows, useReactionActions, useStateStore, useWsConnectionState };
|
|
6065
|
+
export { StreamFeed, StreamFeedContext, StreamFeeds, StreamFeedsContext, useBookmarkActions, useClientConnectedUser, useComments, useCreateFeedsClient, useFeedActivities, useFeedContext, useFeedMetadata, useFeedsClient, useFollowers, useFollowing, useOwnCapabilities, useOwnFollows, useReactionActions, useStateStore, useWsConnectionState };
|
|
5861
6066
|
//# sourceMappingURL=index-react-bindings.browser.js.map
|