@stream-io/feeds-client 0.1.9 → 0.1.11

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.
Files changed (167) hide show
  1. package/@react-bindings/hooks/search-state-hooks/index.ts +3 -0
  2. package/@react-bindings/index.ts +5 -0
  3. package/CHANGELOG.md +20 -0
  4. package/dist/@react-bindings/contexts/StreamFeedContext.d.ts +1 -1
  5. package/dist/@react-bindings/contexts/StreamFeedsContext.d.ts +1 -1
  6. package/dist/@react-bindings/contexts/StreamSearchContext.d.ts +12 -0
  7. package/dist/@react-bindings/contexts/StreamSearchResultsContext.d.ts +12 -0
  8. package/dist/@react-bindings/hooks/feed-state-hooks/useComments.d.ts +1 -1
  9. package/dist/@react-bindings/hooks/feed-state-hooks/useFeedActivities.d.ts +1 -1
  10. package/dist/@react-bindings/hooks/feed-state-hooks/useFeedMetadata.d.ts +1 -1
  11. package/dist/@react-bindings/hooks/feed-state-hooks/useFollowers.d.ts +1 -1
  12. package/dist/@react-bindings/hooks/feed-state-hooks/useFollowing.d.ts +1 -1
  13. package/dist/@react-bindings/hooks/feed-state-hooks/useOwnCapabilities.d.ts +1 -1
  14. package/dist/@react-bindings/hooks/feed-state-hooks/useOwnFollows.d.ts +1 -1
  15. package/dist/@react-bindings/hooks/search-state-hooks/index.d.ts +3 -0
  16. package/dist/@react-bindings/hooks/search-state-hooks/useSearchQuery.d.ts +4 -0
  17. package/dist/@react-bindings/hooks/search-state-hooks/useSearchResult.d.ts +8 -0
  18. package/dist/@react-bindings/hooks/search-state-hooks/useSearchSources.d.ts +4 -0
  19. package/dist/@react-bindings/hooks/useCreateFeedsClient.d.ts +1 -1
  20. package/dist/@react-bindings/index.d.ts +5 -0
  21. package/dist/@react-bindings/wrappers/StreamFeed.d.ts +1 -1
  22. package/dist/@react-bindings/wrappers/StreamSearch.d.ts +12 -0
  23. package/dist/@react-bindings/wrappers/StreamSearchResults.d.ts +12 -0
  24. package/dist/index-react-bindings.browser.cjs +1669 -1529
  25. package/dist/index-react-bindings.browser.cjs.map +1 -1
  26. package/dist/index-react-bindings.browser.js +1661 -1530
  27. package/dist/index-react-bindings.browser.js.map +1 -1
  28. package/dist/index-react-bindings.node.cjs +1669 -1529
  29. package/dist/index-react-bindings.node.cjs.map +1 -1
  30. package/dist/index-react-bindings.node.js +1661 -1530
  31. package/dist/index-react-bindings.node.js.map +1 -1
  32. package/dist/index.browser.cjs +1615 -1640
  33. package/dist/index.browser.cjs.map +1 -1
  34. package/dist/index.browser.js +1613 -1641
  35. package/dist/index.browser.js.map +1 -1
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.node.cjs +1615 -1640
  38. package/dist/index.node.cjs.map +1 -1
  39. package/dist/index.node.js +1613 -1641
  40. package/dist/index.node.js.map +1 -1
  41. package/dist/src/common/ActivitySearchSource.d.ts +1 -1
  42. package/dist/src/common/BaseSearchSource.d.ts +3 -1
  43. package/dist/src/common/FeedSearchSource.d.ts +7 -3
  44. package/dist/src/common/Poll.d.ts +1 -1
  45. package/dist/src/common/SearchController.d.ts +2 -0
  46. package/dist/src/common/UserSearchSource.d.ts +1 -1
  47. package/dist/src/common/real-time/StableWSConnection.d.ts +3 -3
  48. package/dist/src/feed/event-handlers/activity/handle-activity-added.d.ts +7 -0
  49. package/dist/src/feed/event-handlers/activity/handle-activity-deleted.d.ts +8 -0
  50. package/dist/src/feed/event-handlers/activity/handle-activity-reaction-added.d.ts +8 -0
  51. package/dist/src/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts +8 -0
  52. package/dist/src/feed/event-handlers/activity/handle-activity-removed-from-feed.d.ts +3 -0
  53. package/dist/src/feed/event-handlers/activity/handle-activity-updated.d.ts +8 -0
  54. package/dist/src/feed/event-handlers/activity/index.d.ts +6 -0
  55. package/dist/src/feed/event-handlers/bookmark/handle-bookmark-added.d.ts +8 -0
  56. package/dist/src/feed/event-handlers/bookmark/handle-bookmark-deleted.d.ts +9 -0
  57. package/dist/src/feed/event-handlers/bookmark/handle-bookmark-updated.d.ts +8 -0
  58. package/dist/src/feed/event-handlers/bookmark/index.d.ts +3 -0
  59. package/dist/src/feed/event-handlers/comment/handle-comment-added.d.ts +3 -0
  60. package/dist/src/feed/event-handlers/comment/handle-comment-deleted.d.ts +3 -0
  61. package/dist/src/feed/event-handlers/comment/handle-comment-reaction.d.ts +3 -0
  62. package/dist/src/feed/event-handlers/comment/handle-comment-updated.d.ts +3 -0
  63. package/dist/src/feed/event-handlers/comment/index.d.ts +4 -0
  64. package/dist/src/feed/event-handlers/feed/handle-feed-updated.d.ts +3 -0
  65. package/dist/src/feed/event-handlers/feed/index.d.ts +1 -0
  66. package/dist/src/feed/event-handlers/feed-member/handle-feed-member-added.d.ts +3 -0
  67. package/dist/src/feed/event-handlers/feed-member/handle-feed-member-removed.d.ts +3 -0
  68. package/dist/src/feed/event-handlers/feed-member/handle-feed-member-updated.d.ts +3 -0
  69. package/dist/src/feed/event-handlers/feed-member/index.d.ts +3 -0
  70. package/dist/src/feed/event-handlers/follow/handle-follow-created.d.ts +7 -0
  71. package/dist/src/feed/event-handlers/follow/handle-follow-deleted.d.ts +7 -0
  72. package/dist/src/feed/event-handlers/follow/handle-follow-updated.d.ts +3 -0
  73. package/dist/src/feed/event-handlers/follow/index.d.ts +3 -0
  74. package/dist/src/feed/event-handlers/index.d.ts +7 -0
  75. package/dist/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +3 -0
  76. package/dist/src/feed/event-handlers/notification-feed/index.d.ts +1 -0
  77. package/dist/src/{Feed.d.ts → feed/feed.d.ts} +15 -34
  78. package/dist/src/feed/index.d.ts +2 -0
  79. package/dist/src/{FeedsClient.d.ts → feeds-client.d.ts} +6 -5
  80. package/dist/src/gen/models/index.d.ts +5 -0
  81. package/dist/src/gen-imports.d.ts +1 -1
  82. package/dist/src/test-utils/index.d.ts +1 -0
  83. package/dist/src/test-utils/response-generators.d.ts +9 -0
  84. package/dist/src/types-internal.d.ts +7 -0
  85. package/dist/src/types.d.ts +1 -1
  86. package/dist/src/utils/check-has-another-page.d.ts +1 -0
  87. package/dist/src/utils/constants.d.ts +3 -0
  88. package/dist/src/utils/index.d.ts +5 -0
  89. package/dist/src/utils/state-update-queue.d.ts +6 -0
  90. package/dist/src/utils/type-assertions.d.ts +7 -0
  91. package/dist/src/utils/unique-array-merge.d.ts +1 -0
  92. package/dist/tsconfig.tsbuildinfo +1 -1
  93. package/index.ts +2 -2
  94. package/package.json +2 -1
  95. package/src/common/ActivitySearchSource.ts +6 -16
  96. package/src/common/BaseSearchSource.ts +9 -9
  97. package/src/common/FeedSearchSource.ts +22 -67
  98. package/src/common/Poll.ts +1 -1
  99. package/src/common/SearchController.ts +2 -0
  100. package/src/common/UserSearchSource.ts +10 -62
  101. package/src/{state-updates → feed/event-handlers/activity}/activity-reaction-utils.test.ts +12 -2
  102. package/src/{state-updates → feed/event-handlers/activity}/activity-utils.test.ts +3 -2
  103. package/src/{state-updates/activity-utils.ts → feed/event-handlers/activity/handle-activity-added.ts} +16 -36
  104. package/src/feed/event-handlers/activity/handle-activity-deleted.ts +30 -0
  105. package/src/feed/event-handlers/activity/handle-activity-reaction-added.ts +67 -0
  106. package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.ts +75 -0
  107. package/src/feed/event-handlers/activity/handle-activity-removed-from-feed.ts +16 -0
  108. package/src/feed/event-handlers/activity/handle-activity-updated.ts +47 -0
  109. package/src/feed/event-handlers/activity/index.ts +6 -0
  110. package/src/{state-updates → feed/event-handlers/bookmark}/bookmark-utils.test.ts +2 -2
  111. package/src/feed/event-handlers/bookmark/handle-bookmark-added.ts +63 -0
  112. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.ts +84 -0
  113. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.ts +76 -0
  114. package/src/feed/event-handlers/bookmark/index.ts +3 -0
  115. package/src/feed/event-handlers/comment/handle-comment-added.ts +38 -0
  116. package/src/feed/event-handlers/comment/handle-comment-deleted.ts +35 -0
  117. package/src/feed/event-handlers/comment/handle-comment-reaction.ts +61 -0
  118. package/src/feed/event-handlers/comment/handle-comment-updated.ts +35 -0
  119. package/src/feed/event-handlers/comment/index.ts +4 -0
  120. package/src/feed/event-handlers/feed/handle-feed-updated.ts +9 -0
  121. package/src/feed/event-handlers/feed/index.ts +1 -0
  122. package/src/feed/event-handlers/feed-member/handle-feed-member-added.ts +31 -0
  123. package/src/feed/event-handlers/feed-member/handle-feed-member-removed.ts +24 -0
  124. package/src/feed/event-handlers/feed-member/handle-feed-member-updated.ts +40 -0
  125. package/src/feed/event-handlers/feed-member/index.ts +3 -0
  126. package/src/feed/event-handlers/follow/handle-follow-created.test.ts +246 -0
  127. package/src/feed/event-handlers/follow/handle-follow-created.ts +93 -0
  128. package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +264 -0
  129. package/src/feed/event-handlers/follow/handle-follow-deleted.ts +95 -0
  130. package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +174 -0
  131. package/src/feed/event-handlers/follow/handle-follow-updated.ts +88 -0
  132. package/src/feed/event-handlers/follow/index.ts +3 -0
  133. package/src/feed/event-handlers/index.ts +7 -0
  134. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +10 -0
  135. package/src/feed/event-handlers/notification-feed/index.ts +1 -0
  136. package/src/{Feed.ts → feed/feed.ts} +72 -483
  137. package/src/feed/index.ts +2 -0
  138. package/src/{FeedsClient.ts → feeds-client.ts} +26 -8
  139. package/src/gen/model-decoders/decoders.ts +7 -0
  140. package/src/gen/models/index.ts +10 -0
  141. package/src/gen-imports.ts +1 -1
  142. package/src/test-utils/index.ts +1 -0
  143. package/src/test-utils/response-generators.ts +102 -0
  144. package/src/types-internal.ts +11 -0
  145. package/src/types.ts +1 -1
  146. package/src/utils/check-has-another-page.ts +6 -0
  147. package/src/utils/constants.ts +3 -0
  148. package/src/utils/index.ts +5 -0
  149. package/src/{state-updates → utils}/state-update-queue.test.ts +6 -6
  150. package/src/utils/state-update-queue.ts +42 -0
  151. package/src/utils/type-assertions.ts +22 -0
  152. package/src/{utils.test.ts → utils/unique-array-merge.test.ts} +7 -3
  153. package/src/utils/unique-array-merge.ts +19 -0
  154. package/dist/src/state-updates/activity-reaction-utils.d.ts +0 -10
  155. package/dist/src/state-updates/activity-utils.d.ts +0 -13
  156. package/dist/src/state-updates/bookmark-utils.d.ts +0 -14
  157. package/dist/src/state-updates/follow-utils.d.ts +0 -19
  158. package/dist/src/state-updates/state-update-queue.d.ts +0 -15
  159. package/dist/src/utils.d.ts +0 -10
  160. package/src/state-updates/activity-reaction-utils.ts +0 -107
  161. package/src/state-updates/bookmark-utils.ts +0 -167
  162. package/src/state-updates/follow-utils.test.ts +0 -552
  163. package/src/state-updates/follow-utils.ts +0 -126
  164. package/src/state-updates/state-update-queue.ts +0 -35
  165. package/src/utils.ts +0 -48
  166. /package/dist/src/{ModerationClient.d.ts → moderation-client.d.ts} +0 -0
  167. /package/src/{ModerationClient.ts → moderation-client.ts} +0 -0
@@ -0,0 +1,75 @@
1
+ import type { Feed } from '../../../feed';
2
+ import type {
3
+ ActivityReactionDeletedEvent,
4
+ ActivityResponse,
5
+ } from '../../../gen/models';
6
+ import type { EventPayload, UpdateStateResult } from '../../../types-internal';
7
+
8
+ import { updateActivityInState } from './handle-activity-updated';
9
+ export const removeReactionFromActivity = (
10
+ event: ActivityReactionDeletedEvent,
11
+ activity: ActivityResponse,
12
+ isCurrentUser: boolean,
13
+ ): UpdateStateResult<ActivityResponse> => {
14
+ // Update own_reactions if the reaction is from the current user
15
+ const ownReactions = isCurrentUser
16
+ ? (activity.own_reactions || []).filter(
17
+ (r) =>
18
+ !(
19
+ r.type === event.reaction.type &&
20
+ r.user.id === event.reaction.user.id
21
+ ),
22
+ )
23
+ : activity.own_reactions;
24
+
25
+ return {
26
+ ...activity,
27
+ own_reactions: ownReactions,
28
+ latest_reactions: event.activity.latest_reactions,
29
+ reaction_groups: event.activity.reaction_groups,
30
+ changed: true,
31
+ };
32
+ };
33
+
34
+ export const removeReactionFromActivities = (
35
+ event: ActivityReactionDeletedEvent,
36
+ activities: ActivityResponse[] | undefined,
37
+ isCurrentUser: boolean,
38
+ ): UpdateStateResult<{ activities: ActivityResponse[] }> => {
39
+ if (!activities) {
40
+ return { changed: false, activities: [] };
41
+ }
42
+
43
+ const activityIndex = activities.findIndex((a) => a.id === event.activity.id);
44
+ if (activityIndex === -1) {
45
+ return { changed: false, activities };
46
+ }
47
+
48
+ const activity = activities[activityIndex];
49
+ const updatedActivity = removeReactionFromActivity(
50
+ event,
51
+ activity,
52
+ isCurrentUser,
53
+ );
54
+ return updateActivityInState(updatedActivity, activities, true);
55
+ };
56
+
57
+ export function handleActivityReactionDeleted(
58
+ this: Feed,
59
+ event: EventPayload<'feeds.activity.reaction.deleted'>,
60
+ ) {
61
+ const currentActivities = this.currentState.activities;
62
+ const connectedUser = this.client.state.getLatestValue().connected_user;
63
+ const isCurrentUser = Boolean(
64
+ connectedUser && event.reaction.user.id === connectedUser.id,
65
+ );
66
+
67
+ const result = removeReactionFromActivities(
68
+ event,
69
+ currentActivities,
70
+ isCurrentUser,
71
+ );
72
+ if (result.changed) {
73
+ this.state.partialNext({ activities: result.activities });
74
+ }
75
+ }
@@ -0,0 +1,16 @@
1
+ import { Feed } from '../../../feed';
2
+ import { EventPayload } from '../../../types-internal';
3
+ import { removeActivityFromState } from './handle-activity-deleted';
4
+
5
+ export function handleActivityRemovedFromFeed(
6
+ this: Feed,
7
+ event: EventPayload<'feeds.activity.removed_from_feed'>,
8
+ ) {
9
+ const currentActivities = this.currentState.activities;
10
+ if (currentActivities) {
11
+ const result = removeActivityFromState(event.activity, currentActivities);
12
+ if (result.changed) {
13
+ this.state.partialNext({ activities: result.activities });
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,47 @@
1
+ import { Feed } from '../../../feed';
2
+ import { ActivityResponse } from '../../../gen/models';
3
+ import { EventPayload } from '../../../types-internal';
4
+
5
+ export const updateActivityInState = (
6
+ updatedActivityResponse: ActivityResponse,
7
+ activities: ActivityResponse[],
8
+ replaceCompletely: boolean = false,
9
+ ) => {
10
+ const index = activities.findIndex(
11
+ (a) => a.id === updatedActivityResponse.id,
12
+ );
13
+ if (index !== -1) {
14
+ const newActivities = [...activities];
15
+ const activity = activities[index];
16
+
17
+ if (replaceCompletely) {
18
+ newActivities[index] = updatedActivityResponse;
19
+ } else {
20
+ newActivities[index] = {
21
+ ...updatedActivityResponse,
22
+ own_reactions: activity.own_reactions,
23
+ own_bookmarks: activity.own_bookmarks,
24
+ latest_reactions: activity.latest_reactions,
25
+ reaction_groups: activity.reaction_groups,
26
+ };
27
+ }
28
+
29
+ return { changed: true, activities: newActivities };
30
+ } else {
31
+ return { changed: false, activities };
32
+ }
33
+ };
34
+
35
+ export function handleActivityUpdated(
36
+ this: Feed,
37
+ event: EventPayload<'feeds.activity.updated'>,
38
+ ) {
39
+ const currentActivities = this.currentState.activities;
40
+ if (currentActivities) {
41
+ const result = updateActivityInState(event.activity, currentActivities);
42
+ if (result.changed) {
43
+ this.client.hydratePollCache([event.activity]);
44
+ this.state.partialNext({ activities: result.activities });
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,6 @@
1
+ export * from './handle-activity-added';
2
+ export * from './handle-activity-deleted';
3
+ export * from './handle-activity-removed-from-feed';
4
+ export * from './handle-activity-updated';
5
+ export * from './handle-activity-reaction-added';
6
+ export * from './handle-activity-reaction-deleted';
@@ -6,7 +6,7 @@ import {
6
6
  ActivityResponse,
7
7
  BookmarkResponse,
8
8
  UserResponse,
9
- } from '../gen/models';
9
+ } from '../../../gen/models';
10
10
  import {
11
11
  addBookmarkToActivity,
12
12
  removeBookmarkFromActivity,
@@ -14,7 +14,7 @@ import {
14
14
  addBookmarkToActivities,
15
15
  removeBookmarkFromActivities,
16
16
  updateBookmarkInActivities,
17
- } from './bookmark-utils';
17
+ } from './';
18
18
 
19
19
  const createMockUser = (id: string): UserResponse => ({
20
20
  id,
@@ -0,0 +1,63 @@
1
+ import type { Feed } from '../../../feed';
2
+ import type { ActivityResponse, BookmarkAddedEvent } from '../../../gen/models';
3
+ import type { EventPayload, UpdateStateResult } from '../../../types-internal';
4
+
5
+ import { updateActivityInState } from '../activity';
6
+
7
+ export const addBookmarkToActivity = (
8
+ event: BookmarkAddedEvent,
9
+ activity: ActivityResponse,
10
+ isCurrentUser: boolean,
11
+ ): UpdateStateResult<ActivityResponse> => {
12
+ // Update own_bookmarks if the bookmark is from the current user
13
+ const ownBookmarks = [...(activity.own_bookmarks || [])];
14
+ if (isCurrentUser) {
15
+ ownBookmarks.push(event.bookmark);
16
+ }
17
+
18
+ return {
19
+ ...activity,
20
+ own_bookmarks: ownBookmarks,
21
+ changed: true,
22
+ };
23
+ };
24
+
25
+ export const addBookmarkToActivities = (
26
+ event: BookmarkAddedEvent,
27
+ activities: ActivityResponse[] | undefined,
28
+ isCurrentUser: boolean,
29
+ ): UpdateStateResult<{ activities: ActivityResponse[] }> => {
30
+ if (!activities) {
31
+ return { changed: false, activities: [] };
32
+ }
33
+
34
+ const activityIndex = activities.findIndex(
35
+ (a) => a.id === event.bookmark.activity.id,
36
+ );
37
+ if (activityIndex === -1) {
38
+ return { changed: false, activities };
39
+ }
40
+
41
+ const activity = activities[activityIndex];
42
+ const updatedActivity = addBookmarkToActivity(event, activity, isCurrentUser);
43
+ return updateActivityInState(updatedActivity, activities, true);
44
+ };
45
+
46
+ export function handleBookmarkAdded(
47
+ this: Feed,
48
+ event: EventPayload<'feeds.bookmark.added'>,
49
+ ) {
50
+ const currentActivities = this.currentState.activities;
51
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
52
+ const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
53
+
54
+ const result = addBookmarkToActivities(
55
+ event,
56
+ currentActivities,
57
+ isCurrentUser,
58
+ );
59
+
60
+ if (result.changed) {
61
+ this.state.partialNext({ activities: result.activities });
62
+ }
63
+ }
@@ -0,0 +1,84 @@
1
+ import type { Feed } from '../../../feed';
2
+ import type {
3
+ ActivityResponse,
4
+ BookmarkDeletedEvent,
5
+ BookmarkResponse,
6
+ } from '../../../gen/models';
7
+ import type { EventPayload, UpdateStateResult } from '../../../types-internal';
8
+
9
+ import { updateActivityInState } from '../activity';
10
+
11
+ // Helper function to check if two bookmarks are the same
12
+ // A bookmark is identified by activity_id + folder_id + user_id
13
+ export const isSameBookmark = (
14
+ bookmark1: BookmarkResponse,
15
+ bookmark2: BookmarkResponse,
16
+ ): boolean => {
17
+ return (
18
+ bookmark1.user.id === bookmark2.user.id &&
19
+ bookmark1.activity.id === bookmark2.activity.id &&
20
+ bookmark1.folder?.id === bookmark2.folder?.id
21
+ );
22
+ };
23
+
24
+ export const removeBookmarkFromActivities = (
25
+ event: BookmarkDeletedEvent,
26
+ activities: ActivityResponse[] | undefined,
27
+ isCurrentUser: boolean,
28
+ ): UpdateStateResult<{ activities: ActivityResponse[] }> => {
29
+ if (!activities) {
30
+ return { changed: false, activities: [] };
31
+ }
32
+
33
+ const activityIndex = activities.findIndex(
34
+ (a) => a.id === event.bookmark.activity.id,
35
+ );
36
+ if (activityIndex === -1) {
37
+ return { changed: false, activities };
38
+ }
39
+
40
+ const activity = activities[activityIndex];
41
+ const updatedActivity = removeBookmarkFromActivity(
42
+ event,
43
+ activity,
44
+ isCurrentUser,
45
+ );
46
+ return updateActivityInState(updatedActivity, activities, true);
47
+ };
48
+
49
+ export const removeBookmarkFromActivity = (
50
+ event: BookmarkDeletedEvent,
51
+ activity: ActivityResponse,
52
+ isCurrentUser: boolean,
53
+ ): UpdateStateResult<ActivityResponse> => {
54
+ // Update own_bookmarks if the bookmark is from the current user
55
+ const ownBookmarks = isCurrentUser
56
+ ? (activity.own_bookmarks || []).filter(
57
+ (bookmark) => !isSameBookmark(bookmark, event.bookmark),
58
+ )
59
+ : activity.own_bookmarks;
60
+
61
+ return {
62
+ ...activity,
63
+ own_bookmarks: ownBookmarks,
64
+ changed: true,
65
+ };
66
+ };
67
+
68
+ export function handleBookmarkDeleted(
69
+ this: Feed,
70
+ event: EventPayload<'feeds.bookmark.deleted'>,
71
+ ) {
72
+ const currentActivities = this.currentState.activities;
73
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
74
+ const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
75
+
76
+ const result = removeBookmarkFromActivities(
77
+ event,
78
+ currentActivities,
79
+ isCurrentUser,
80
+ );
81
+ if (result.changed) {
82
+ this.state.partialNext({ activities: result.activities });
83
+ }
84
+ }
@@ -0,0 +1,76 @@
1
+ import type { Feed } from '../../../feed';
2
+ import type {
3
+ ActivityResponse,
4
+ BookmarkUpdatedEvent,
5
+ } from '../../../gen/models';
6
+ import type { EventPayload, UpdateStateResult } from '../../../types-internal';
7
+
8
+ import { updateActivityInState } from '../activity';
9
+ import { isSameBookmark } from './handle-bookmark-deleted';
10
+
11
+ export const updateBookmarkInActivity = (
12
+ event: BookmarkUpdatedEvent,
13
+ activity: ActivityResponse,
14
+ isCurrentUser: boolean,
15
+ ): UpdateStateResult<ActivityResponse> => {
16
+ // Update own_bookmarks if the bookmark is from the current user
17
+ let ownBookmarks = activity.own_bookmarks || [];
18
+ if (isCurrentUser) {
19
+ const bookmarkIndex = ownBookmarks.findIndex((bookmark) =>
20
+ isSameBookmark(bookmark, event.bookmark),
21
+ );
22
+ if (bookmarkIndex !== -1) {
23
+ ownBookmarks = [...ownBookmarks];
24
+ ownBookmarks[bookmarkIndex] = event.bookmark;
25
+ }
26
+ }
27
+
28
+ return {
29
+ ...activity,
30
+ own_bookmarks: ownBookmarks,
31
+ changed: true,
32
+ };
33
+ };
34
+
35
+ export const updateBookmarkInActivities = (
36
+ event: BookmarkUpdatedEvent,
37
+ activities: ActivityResponse[] | undefined,
38
+ isCurrentUser: boolean,
39
+ ): UpdateStateResult<{ activities: ActivityResponse[] }> => {
40
+ if (!activities) {
41
+ return { changed: false, activities: [] };
42
+ }
43
+
44
+ const activityIndex = activities.findIndex(
45
+ (a) => a.id === event.bookmark.activity.id,
46
+ );
47
+ if (activityIndex === -1) {
48
+ return { changed: false, activities };
49
+ }
50
+
51
+ const activity = activities[activityIndex];
52
+ const updatedActivity = updateBookmarkInActivity(
53
+ event,
54
+ activity,
55
+ isCurrentUser,
56
+ );
57
+ return updateActivityInState(updatedActivity, activities, true);
58
+ };
59
+
60
+ export function handleBookmarkUpdated(
61
+ this: Feed,
62
+ event: EventPayload<'feeds.bookmark.updated'>,
63
+ ) {
64
+ const currentActivities = this.currentState.activities;
65
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
66
+ const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
67
+
68
+ const result = updateBookmarkInActivities(
69
+ event,
70
+ currentActivities,
71
+ isCurrentUser,
72
+ );
73
+ if (result.changed) {
74
+ this.state.partialNext({ activities: result.activities });
75
+ }
76
+ }
@@ -0,0 +1,3 @@
1
+ export * from './handle-bookmark-added';
2
+ export * from './handle-bookmark-deleted';
3
+ export * from './handle-bookmark-updated';
@@ -0,0 +1,38 @@
1
+ import type { Feed } from '../../../feed';
2
+ import type { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleCommentAdded(
5
+ this: Feed,
6
+ event: EventPayload<'feeds.comment.added'>,
7
+ ) {
8
+ const { comment } = event;
9
+ const entityId = comment.parent_id ?? comment.object_id;
10
+
11
+ this.state.next((currentState) => {
12
+ const entityState = currentState.comments_by_entity_id[entityId];
13
+
14
+ if (typeof entityState?.comments === 'undefined') {
15
+ return currentState;
16
+ }
17
+
18
+ const newComments = entityState?.comments ? [...entityState.comments] : [];
19
+
20
+ if (entityState.pagination?.sort === 'last') {
21
+ newComments.unshift(comment);
22
+ } else {
23
+ // 'first' and other sort options
24
+ newComments.push(comment);
25
+ }
26
+
27
+ return {
28
+ ...currentState,
29
+ comments_by_entity_id: {
30
+ ...currentState.comments_by_entity_id,
31
+ [entityId]: {
32
+ ...currentState.comments_by_entity_id[entityId],
33
+ comments: newComments,
34
+ },
35
+ },
36
+ };
37
+ });
38
+ }
@@ -0,0 +1,35 @@
1
+ import { Feed } from '../../../feed';
2
+ import { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleCommentDeleted(
5
+ this: Feed,
6
+ { comment }: EventPayload<'feeds.comment.deleted'>,
7
+ ) {
8
+ const entityId = comment.parent_id ?? comment.object_id;
9
+
10
+ this.state.next((currentState) => {
11
+ const newCommentsByEntityId = {
12
+ ...currentState.comments_by_entity_id,
13
+ [entityId]: {
14
+ ...currentState.comments_by_entity_id[entityId],
15
+ },
16
+ };
17
+
18
+ const index = this.getCommentIndex(comment, currentState);
19
+
20
+ if (newCommentsByEntityId?.[entityId]?.comments?.length && index !== -1) {
21
+ newCommentsByEntityId[entityId].comments = [
22
+ ...newCommentsByEntityId[entityId].comments,
23
+ ];
24
+
25
+ newCommentsByEntityId[entityId]?.comments?.splice(index, 1);
26
+ }
27
+
28
+ delete newCommentsByEntityId[comment.id];
29
+
30
+ return {
31
+ ...currentState,
32
+ comments_by_entity_id: newCommentsByEntityId,
33
+ };
34
+ });
35
+ }
@@ -0,0 +1,61 @@
1
+ import { Feed } from '../../../feed';
2
+ import { CommentResponse } from '../../../gen/models';
3
+ import { EventPayload } from '../../../types-internal';
4
+
5
+ export function handleCommentReaction(
6
+ this: Feed,
7
+ event: EventPayload<
8
+ 'feeds.comment.reaction.added' | 'feeds.comment.reaction.deleted'
9
+ >,
10
+ ) {
11
+ const { comment, reaction } = event;
12
+ const connectedUser = this.client.state.getLatestValue().connected_user;
13
+
14
+ this.state.next((currentState) => {
15
+ const forId = comment.parent_id ?? comment.object_id;
16
+ const entityState = currentState.comments_by_entity_id[forId];
17
+
18
+ const commentIndex = this.getCommentIndex(comment, currentState);
19
+
20
+ if (commentIndex === -1) return currentState;
21
+
22
+ const newComments = entityState?.comments?.concat([]) ?? [];
23
+
24
+ const commentCopy: Partial<CommentResponse> = { ...comment };
25
+
26
+ delete commentCopy.own_reactions;
27
+
28
+ const newComment: CommentResponse = {
29
+ ...newComments[commentIndex],
30
+ ...commentCopy,
31
+ // TODO: FIXME this should be handled by the backend
32
+ latest_reactions: commentCopy.latest_reactions ?? [],
33
+ reaction_groups: commentCopy.reaction_groups ?? {},
34
+ };
35
+
36
+ newComments[commentIndex] = newComment;
37
+
38
+ if (reaction.user.id === connectedUser?.id) {
39
+ if (event.type === 'feeds.comment.reaction.added') {
40
+ newComment.own_reactions = newComment.own_reactions.concat(
41
+ reaction,
42
+ ) ?? [reaction];
43
+ } else if (event.type === 'feeds.comment.reaction.deleted') {
44
+ newComment.own_reactions = newComment.own_reactions.filter(
45
+ (r) => r.type !== reaction.type,
46
+ );
47
+ }
48
+ }
49
+
50
+ return {
51
+ ...currentState,
52
+ comments_by_entity_id: {
53
+ ...currentState.comments_by_entity_id,
54
+ [forId]: {
55
+ ...entityState,
56
+ comments: newComments,
57
+ },
58
+ },
59
+ };
60
+ });
61
+ }
@@ -0,0 +1,35 @@
1
+ import { Feed } from '../../../feed';
2
+ import { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleCommentUpdated(
5
+ this: Feed,
6
+ event: EventPayload<'feeds.comment.updated'>,
7
+ ) {
8
+ const { comment } = event;
9
+ const entityId = comment.parent_id ?? comment.object_id;
10
+
11
+ this.state.next((currentState) => {
12
+ const entityState = currentState.comments_by_entity_id[entityId];
13
+
14
+ if (!entityState?.comments?.length) return currentState;
15
+
16
+ const index = this.getCommentIndex(comment, currentState);
17
+
18
+ if (index === -1) return currentState;
19
+
20
+ const newComments = [...entityState.comments];
21
+
22
+ newComments[index] = comment;
23
+
24
+ return {
25
+ ...currentState,
26
+ comments_by_entity_id: {
27
+ ...currentState.comments_by_entity_id,
28
+ [entityId]: {
29
+ ...currentState.comments_by_entity_id[entityId],
30
+ comments: newComments,
31
+ },
32
+ },
33
+ };
34
+ });
35
+ }
@@ -0,0 +1,4 @@
1
+ export * from './handle-comment-added';
2
+ export * from './handle-comment-deleted';
3
+ export * from './handle-comment-updated';
4
+ export * from './handle-comment-reaction';
@@ -0,0 +1,9 @@
1
+ import { Feed } from '../../../feed';
2
+ import { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleFeedUpdated(
5
+ this: Feed,
6
+ event: EventPayload<'feeds.feed.updated'>,
7
+ ) {
8
+ this.state.partialNext({ ...event.feed });
9
+ }
@@ -0,0 +1 @@
1
+ export * from './handle-feed-updated';
@@ -0,0 +1,31 @@
1
+ import { Feed, FeedState } from '../../../feed';
2
+ import { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleFeedMemberAdded(
5
+ this: Feed,
6
+ event: EventPayload<'feeds.feed_member.added'>,
7
+ ) {
8
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
9
+
10
+ this.state.next((currentState) => {
11
+ let newState: FeedState | undefined;
12
+
13
+ if (typeof currentState.members !== 'undefined') {
14
+ newState ??= {
15
+ ...currentState,
16
+ };
17
+
18
+ newState.members = [event.member, ...currentState.members];
19
+ }
20
+
21
+ if (connectedUser?.id === event.member.user.id) {
22
+ newState ??= {
23
+ ...currentState,
24
+ };
25
+
26
+ newState.own_membership = event.member;
27
+ }
28
+
29
+ return newState ?? currentState;
30
+ });
31
+ }
@@ -0,0 +1,24 @@
1
+ import { Feed } from '../../../feed';
2
+ import { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleFeedMemberRemoved(
5
+ this: Feed,
6
+ event: EventPayload<'feeds.feed_member.removed'>,
7
+ ) {
8
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
9
+
10
+ this.state.next((currentState) => {
11
+ const newState = {
12
+ ...currentState,
13
+ members: currentState.members?.filter(
14
+ (member) => member.user.id !== event.user?.id,
15
+ ),
16
+ };
17
+
18
+ if (connectedUser?.id === event.member_id) {
19
+ delete newState.own_membership;
20
+ }
21
+
22
+ return newState;
23
+ });
24
+ }
@@ -0,0 +1,40 @@
1
+ import { Feed, FeedState } from '../../../feed';
2
+ import { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleFeedMemberUpdated(
5
+ this: Feed,
6
+ event: EventPayload<'feeds.feed_member.updated'>,
7
+ ) {
8
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
9
+
10
+ this.state.next((currentState) => {
11
+ const memberIndex =
12
+ currentState.members?.findIndex(
13
+ (member) => member.user.id === event.member.user.id,
14
+ ) ?? -1;
15
+
16
+ let newState: FeedState | undefined;
17
+
18
+ if (memberIndex !== -1) {
19
+ // if there's an index, there's a member to update
20
+ const newMembers = [...currentState.members!];
21
+ newMembers[memberIndex] = event.member;
22
+
23
+ newState ??= {
24
+ ...currentState,
25
+ };
26
+
27
+ newState.members = newMembers;
28
+ }
29
+
30
+ if (connectedUser?.id === event.member.user.id) {
31
+ newState ??= {
32
+ ...currentState,
33
+ };
34
+
35
+ newState.own_membership = event.member;
36
+ }
37
+
38
+ return newState ?? currentState;
39
+ });
40
+ }