@stream-io/feeds-client 1.0.0 → 1.2.0

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 (98) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/index.js +477 -466
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/react-bindings.js +454 -463
  5. package/dist/cjs/react-bindings.js.map +1 -1
  6. package/dist/es/index.mjs +440 -466
  7. package/dist/es/index.mjs.map +1 -1
  8. package/dist/es/react-bindings.mjs +447 -493
  9. package/dist/es/react-bindings.mjs.map +1 -1
  10. package/dist/feeds-client-ACVPbpUP.mjs +8752 -0
  11. package/dist/feeds-client-ACVPbpUP.mjs.map +1 -0
  12. package/dist/feeds-client-BP0fE4NZ.js +8918 -0
  13. package/dist/feeds-client-BP0fE4NZ.js.map +1 -0
  14. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  15. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts +6 -2
  16. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts.map +1 -1
  17. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivitySeen.d.ts +6 -2
  18. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivitySeen.d.ts.map +1 -1
  19. package/dist/types/feed/event-handlers/activity/handle-activity-deleted.d.ts +3 -2
  20. package/dist/types/feed/event-handlers/activity/handle-activity-deleted.d.ts.map +1 -1
  21. package/dist/types/feed/event-handlers/activity/handle-activity-feedback.d.ts +5 -0
  22. package/dist/types/feed/event-handlers/activity/handle-activity-feedback.d.ts.map +1 -1
  23. package/dist/types/feed/event-handlers/activity/handle-activity-pinned.d.ts +3 -2
  24. package/dist/types/feed/event-handlers/activity/handle-activity-pinned.d.ts.map +1 -1
  25. package/dist/types/feed/event-handlers/activity/handle-activity-unpinned.d.ts +3 -2
  26. package/dist/types/feed/event-handlers/activity/handle-activity-unpinned.d.ts.map +1 -1
  27. package/dist/types/feed/event-handlers/activity/index.d.ts +3 -1
  28. package/dist/types/feed/event-handlers/activity/index.d.ts.map +1 -1
  29. package/dist/types/feed/event-handlers/activity-updater.d.ts +1 -0
  30. package/dist/types/feed/event-handlers/activity-updater.d.ts.map +1 -1
  31. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts +1 -1
  32. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts.map +1 -1
  33. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-added.d.ts +6 -5
  34. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-added.d.ts.map +1 -1
  35. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-deleted.d.ts +6 -5
  36. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-deleted.d.ts.map +1 -1
  37. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-updated.d.ts +6 -5
  38. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-updated.d.ts.map +1 -1
  39. package/dist/types/feed/event-handlers/feed/handle-feed-deleted.d.ts +4 -0
  40. package/dist/types/feed/event-handlers/feed/handle-feed-deleted.d.ts.map +1 -0
  41. package/dist/types/feed/event-handlers/feed/handle-feed-updated.d.ts.map +1 -1
  42. package/dist/types/feed/event-handlers/feed/index.d.ts +1 -0
  43. package/dist/types/feed/event-handlers/feed/index.d.ts.map +1 -1
  44. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-added.d.ts +3 -2
  45. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-added.d.ts.map +1 -1
  46. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-removed.d.ts +3 -2
  47. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-removed.d.ts.map +1 -1
  48. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-updated.d.ts +3 -2
  49. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-updated.d.ts.map +1 -1
  50. package/dist/types/feed/event-handlers/follow/handle-follow-updated.d.ts.map +1 -1
  51. package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +3 -2
  52. package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map +1 -1
  53. package/dist/types/feed/feed.d.ts.map +1 -1
  54. package/dist/types/feeds-client/feeds-client.d.ts +25 -1
  55. package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
  56. package/dist/types/gen/feeds/FeedsApi.d.ts +6 -4
  57. package/dist/types/gen/feeds/FeedsApi.d.ts.map +1 -1
  58. package/dist/types/gen/models/index.d.ts +40 -2
  59. package/dist/types/gen/models/index.d.ts.map +1 -1
  60. package/dist/types/utils/state-update-queue.d.ts +11 -2
  61. package/dist/types/utils/state-update-queue.d.ts.map +1 -1
  62. package/dist/types/utils/unique-array-merge.d.ts +1 -1
  63. package/dist/types/utils/unique-array-merge.d.ts.map +1 -1
  64. package/package.json +15 -15
  65. package/src/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.ts +6 -20
  66. package/src/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivitySeen.ts +6 -20
  67. package/src/feed/event-handlers/activity/handle-activity-deleted.ts +28 -2
  68. package/src/feed/event-handlers/activity/handle-activity-feedback.ts +17 -7
  69. package/src/feed/event-handlers/activity/handle-activity-pinned.ts +25 -3
  70. package/src/feed/event-handlers/activity/handle-activity-unpinned.ts +25 -2
  71. package/src/feed/event-handlers/activity/index.ts +3 -1
  72. package/src/feed/event-handlers/add-aggregated-activities-to-state.ts +11 -2
  73. package/src/feed/event-handlers/bookmark/handle-bookmark-added.ts +20 -11
  74. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.ts +21 -11
  75. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.ts +24 -10
  76. package/src/feed/event-handlers/feed/handle-feed-deleted.ts +12 -0
  77. package/src/feed/event-handlers/feed/handle-feed-updated.ts +8 -0
  78. package/src/feed/event-handlers/feed/index.ts +1 -0
  79. package/src/feed/event-handlers/feed-member/handle-feed-member-added.ts +25 -2
  80. package/src/feed/event-handlers/feed-member/handle-feed-member-removed.ts +25 -2
  81. package/src/feed/event-handlers/feed-member/handle-feed-member-updated.ts +25 -2
  82. package/src/feed/event-handlers/follow/handle-follow-updated.ts +14 -0
  83. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +64 -2
  84. package/src/feed/event-handlers/story-feeds/handle-story-feeds-updated.ts +1 -1
  85. package/src/feed/feed.ts +7 -5
  86. package/src/feeds-client/feeds-client.ts +359 -45
  87. package/src/gen/feeds/FeedsApi.ts +79 -12
  88. package/src/gen/model-decoders/decoders.ts +7 -0
  89. package/src/gen/models/index.ts +66 -4
  90. package/src/utils/state-update-queue.ts +42 -28
  91. package/src/utils/unique-array-merge.ts +11 -3
  92. package/dist/feeds-client-B03y08Kq.mjs +0 -8409
  93. package/dist/feeds-client-B03y08Kq.mjs.map +0 -1
  94. package/dist/feeds-client-tw63OGrd.js +0 -8425
  95. package/dist/feeds-client-tw63OGrd.js.map +0 -1
  96. package/dist/types/feed/event-handlers/activity/handle-activity-marked.d.ts +0 -12
  97. package/dist/types/feed/event-handlers/activity/handle-activity-marked.d.ts.map +0 -1
  98. package/src/feed/event-handlers/activity/handle-activity-marked.ts +0 -68
@@ -1,30 +1,16 @@
1
- import { useMemo } from 'react';
2
-
3
- import { useFeedContext } from '../../contexts/StreamFeedContext';
4
- import { useNotificationStatus } from './useNotificationStatus';
5
1
  import type { Feed } from '../../../../feed';
6
2
  import type { AggregatedActivityResponse } from '../../../../gen/models';
7
3
 
4
+ /**
5
+ * @deprecated use aggregatedActivity.is_read instead
6
+ * @returns
7
+ */
8
8
  export const useIsAggregatedActivityRead = ({
9
- feed: feedFromProps,
9
+ feed: _,
10
10
  aggregatedActivity,
11
11
  }: {
12
12
  feed?: Feed;
13
13
  aggregatedActivity: AggregatedActivityResponse;
14
14
  }) => {
15
- const feedFromContext = useFeedContext();
16
- const feed = feedFromProps ?? feedFromContext;
17
-
18
- const { read_activities: readActivities, last_read_at: lastReadAt } =
19
- useNotificationStatus(feed) ?? {};
20
-
21
- const group = aggregatedActivity.group;
22
-
23
- return useMemo(
24
- () =>
25
- (lastReadAt &&
26
- aggregatedActivity.updated_at.getTime() <= lastReadAt.getTime()) ||
27
- (readActivities ?? []).includes(group),
28
- [lastReadAt, aggregatedActivity.updated_at, readActivities, group],
29
- );
15
+ return aggregatedActivity.is_read;
30
16
  };
@@ -1,30 +1,16 @@
1
- import { useMemo } from 'react';
2
-
3
- import { useFeedContext } from '../../contexts/StreamFeedContext';
4
- import { useNotificationStatus } from './useNotificationStatus';
5
1
  import type { Feed } from '../../../../feed';
6
2
  import type { AggregatedActivityResponse } from '../../../../gen/models';
7
3
 
4
+ /**
5
+ * @deprecated use aggregatedActivity.is_seen instead
6
+ * @returns
7
+ */
8
8
  export const useIsAggregatedActivitySeen = ({
9
- feed: feedFromProps,
9
+ feed: _,
10
10
  aggregatedActivity,
11
11
  }: {
12
12
  feed?: Feed;
13
13
  aggregatedActivity: AggregatedActivityResponse;
14
14
  }) => {
15
- const feedFromContext = useFeedContext();
16
- const feed = feedFromProps ?? feedFromContext;
17
-
18
- const { seen_activities: seenActivities, last_seen_at: lastSeenAt } =
19
- useNotificationStatus(feed) ?? {};
20
-
21
- const group = aggregatedActivity.group;
22
-
23
- return useMemo(
24
- () =>
25
- (lastSeenAt &&
26
- aggregatedActivity.updated_at.getTime() < lastSeenAt.getTime()) ||
27
- (seenActivities ?? []).includes(group),
28
- [lastSeenAt, aggregatedActivity.updated_at, seenActivities, group],
29
- );
15
+ return aggregatedActivity.is_seen;
30
16
  };
@@ -3,7 +3,13 @@ import type {
3
3
  ActivityPinResponse,
4
4
  ActivityResponse,
5
5
  } from '../../../gen/models';
6
- import type { EventPayload, UpdateStateResult } from '../../../types-internal';
6
+ import type {
7
+ EventPayload,
8
+ PartializeAllBut,
9
+ UpdateStateResult,
10
+ } from '../../../types-internal';
11
+ import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
12
+ import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
7
13
 
8
14
  export function removeActivityFromState(
9
15
  this: Feed,
@@ -45,10 +51,30 @@ export const removePinnedActivityFromState = (
45
51
  }
46
52
  };
47
53
 
54
+ export type ActivityDeletedPayload = PartializeAllBut<
55
+ EventPayload<'feeds.activity.deleted'>,
56
+ 'activity'
57
+ >;
58
+
48
59
  export function handleActivityDeleted(
49
60
  this: Feed,
50
- event: EventPayload<'feeds.activity.deleted'>,
61
+ event: ActivityDeletedPayload,
62
+ fromWs?: boolean,
51
63
  ) {
64
+ if (
65
+ !shouldUpdateState({
66
+ stateUpdateQueueId: getStateUpdateQueueId(event, 'activity-deleted'),
67
+ stateUpdateQueue: this.stateUpdateQueue,
68
+ watch: this.currentState.watch,
69
+ fromWs,
70
+ isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(
71
+ this,
72
+ event,
73
+ ),
74
+ })
75
+ ) {
76
+ return;
77
+ }
52
78
  const {
53
79
  activities: currentActivities,
54
80
  pinned_activities: currentPinnedActivities,
@@ -2,12 +2,16 @@ import { updateEntityInArray } from '../../../utils';
2
2
  import { isPin } from '../is-activity-pin';
3
3
  import type { Feed } from '../../feed';
4
4
  import type { EventPayload } from '../../../types-internal';
5
- import type { ActivityFeedbackEventPayload, ActivityPinResponse, ActivityResponse } from '../../../gen/models';
5
+ import type {
6
+ ActivityFeedbackEventPayload,
7
+ ActivityPinResponse,
8
+ ActivityResponse,
9
+ } from '../../../gen/models';
6
10
 
7
- const updateActivityFromFeedback = <
11
+ export const updateActivityFromFeedback = <
8
12
  T extends ActivityResponse | ActivityPinResponse,
9
13
  >(
10
- feedback: ActivityFeedbackEventPayload,
14
+ feedback: Pick<ActivityFeedbackEventPayload, 'activity_id' | 'value'>,
11
15
  activities: T[] | undefined,
12
16
  ) => {
13
17
  if (!activities) {
@@ -18,10 +22,16 @@ const updateActivityFromFeedback = <
18
22
  }
19
23
  return updateEntityInArray<T>({
20
24
  entities: activities,
21
- matcher: (e) =>
22
- isPin(e)
23
- ? e.activity.id === feedback.activity_id
24
- : e.id === feedback.activity_id,
25
+ matcher: (e) => {
26
+ const newHidden = feedback.value === 'true';
27
+ if (isPin(e)) {
28
+ return (
29
+ e.activity.id === feedback.activity_id &&
30
+ e.activity.hidden !== newHidden
31
+ );
32
+ }
33
+ return e.id === feedback.activity_id && e.hidden !== newHidden;
34
+ },
25
35
  updater: (e) => {
26
36
  if (isPin(e)) {
27
37
  return {
@@ -1,11 +1,34 @@
1
1
  import type { ActivityPinResponse } from '../../../gen/models';
2
- import type { EventPayload } from '../../../types-internal';
2
+ import type { EventPayload, PartializeAllBut } from '../../../types-internal';
3
3
  import type { Feed } from '../../feed';
4
+ import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
5
+ import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
6
+
7
+ export type ActivityPinnedPayload = PartializeAllBut<
8
+ EventPayload<'feeds.activity.pinned'>,
9
+ 'pinned_activity'
10
+ >;
4
11
 
5
12
  export function handleActivityPinned(
6
13
  this: Feed,
7
- event: EventPayload<'feeds.activity.pinned'>,
14
+ event: ActivityPinnedPayload,
15
+ fromWs?: boolean,
8
16
  ) {
17
+ if (
18
+ !shouldUpdateState({
19
+ stateUpdateQueueId: getStateUpdateQueueId(event, 'activity-pinned'),
20
+ stateUpdateQueue: this.stateUpdateQueue,
21
+ watch: this.currentState.watch,
22
+ fromWs,
23
+ isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(
24
+ this,
25
+ event,
26
+ ),
27
+ })
28
+ ) {
29
+ return;
30
+ }
31
+
9
32
  this.state.next((currentState) => {
10
33
  const newState = {
11
34
  ...currentState,
@@ -17,7 +40,6 @@ export function handleActivityPinned(
17
40
  const pinnedActivity: ActivityPinResponse = {
18
41
  ...event.pinned_activity,
19
42
  user: event.user!,
20
- feed: event.fid,
21
43
  updated_at: new Date(),
22
44
  };
23
45
 
@@ -1,10 +1,33 @@
1
- import type { EventPayload } from '../../../types-internal';
1
+ import type { EventPayload, PartializeAllBut } from '../../../types-internal';
2
2
  import type { Feed, FeedState } from '../../feed';
3
+ import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
4
+ import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
5
+
6
+ export type ActivityUnpinnedPayload = PartializeAllBut<
7
+ EventPayload<'feeds.activity.unpinned'>,
8
+ 'pinned_activity'
9
+ >;
3
10
 
4
11
  export function handleActivityUnpinned(
5
12
  this: Feed,
6
- event: EventPayload<'feeds.activity.unpinned'>,
13
+ event: ActivityUnpinnedPayload,
14
+ fromWs?: boolean,
7
15
  ) {
16
+ if (
17
+ !shouldUpdateState({
18
+ stateUpdateQueueId: getStateUpdateQueueId(event, 'activity-unpinned'),
19
+ stateUpdateQueue: this.stateUpdateQueue,
20
+ watch: this.currentState.watch,
21
+ fromWs,
22
+ isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(
23
+ this,
24
+ event,
25
+ ),
26
+ })
27
+ ) {
28
+ return;
29
+ }
30
+
8
31
  this.state.next((currentState) => {
9
32
  let newState: FeedState | undefined;
10
33
 
@@ -5,4 +5,6 @@ export * from './handle-activity-updated';
5
5
  export * from './handle-activity-reaction-added';
6
6
  export * from './handle-activity-reaction-deleted';
7
7
  export * from './handle-activity-reaction-updated';
8
- export * from './handle-activity-marked';
8
+ export * from './handle-activity-pinned';
9
+ export * from './handle-activity-unpinned';
10
+ export * from './handle-activity-feedback';
@@ -6,7 +6,7 @@ import { updateActivity } from './activity-updater';
6
6
  export const addAggregatedActivitiesToState = (
7
7
  newAggregatedActivities: AggregatedActivityResponse[],
8
8
  aggregatedActivities: AggregatedActivityResponse[] | undefined,
9
- position: 'start' | 'end' | 'replace',
9
+ position: 'start' | 'end' | 'replace-then-end' | 'replace-then-start',
10
10
  ) => {
11
11
  let result: UpdateStateResult<{
12
12
  aggregated_activities: AggregatedActivityResponse[];
@@ -58,11 +58,20 @@ export const addAggregatedActivitiesToState = (
58
58
  (a) => a.group,
59
59
  );
60
60
  break;
61
- case 'replace':
61
+ case 'replace-then-end':
62
62
  result.aggregated_activities = replaceUniqueArrayMerge(
63
63
  aggregatedActivities ?? [],
64
64
  newAggregatedActivities,
65
65
  (a) => a.group,
66
+ 'end',
67
+ );
68
+ break;
69
+ case 'replace-then-start':
70
+ result.aggregated_activities = replaceUniqueArrayMerge(
71
+ aggregatedActivities ?? [],
72
+ newAggregatedActivities,
73
+ (a) => a.group,
74
+ 'start',
66
75
  );
67
76
  break;
68
77
  }
@@ -2,10 +2,15 @@ import type { Feed } from '../../../feed';
2
2
  import type {
3
3
  ActivityPinResponse,
4
4
  ActivityResponse,
5
- BookmarkAddedEvent,
6
5
  } from '../../../gen/models';
7
- import type { EventPayload } from '../../../types-internal';
6
+ import type { EventPayload, PartializeAllBut } from '../../../types-internal';
8
7
  import { updateEntityInArray } from '../../../utils';
8
+ import { isSameBookmark } from './handle-bookmark-deleted';
9
+
10
+ export type BookmarkAddedPayload = PartializeAllBut<
11
+ EventPayload<'feeds.bookmark.added'>,
12
+ 'bookmark'
13
+ >;
9
14
 
10
15
  const sharedUpdateActivity = ({
11
16
  currentActivity,
@@ -13,7 +18,7 @@ const sharedUpdateActivity = ({
13
18
  eventBelongsToCurrentUser,
14
19
  }: {
15
20
  currentActivity: ActivityResponse;
16
- event: BookmarkAddedEvent;
21
+ event: BookmarkAddedPayload;
17
22
  eventBelongsToCurrentUser: boolean;
18
23
  }): ActivityResponse => {
19
24
  let newOwnBookmarks = currentActivity.own_bookmarks;
@@ -30,13 +35,16 @@ const sharedUpdateActivity = ({
30
35
  };
31
36
 
32
37
  export const addBookmarkToActivities = (
33
- event: BookmarkAddedEvent,
38
+ event: BookmarkAddedPayload,
34
39
  activities: ActivityResponse[] | undefined,
35
40
  eventBelongsToCurrentUser: boolean,
36
41
  ) =>
37
42
  updateEntityInArray({
38
43
  entities: activities,
39
- matcher: (activity) => activity.id === event.bookmark.activity.id,
44
+ matcher: (activity) =>
45
+ activity.id === event.bookmark.activity.id &&
46
+ (!eventBelongsToCurrentUser ||
47
+ !activity.own_bookmarks.some((b) => isSameBookmark(b, event.bookmark))),
40
48
  updater: (matchedActivity) =>
41
49
  sharedUpdateActivity({
42
50
  currentActivity: matchedActivity,
@@ -46,14 +54,18 @@ export const addBookmarkToActivities = (
46
54
  });
47
55
 
48
56
  export const addBookmarkToPinnedActivities = (
49
- event: BookmarkAddedEvent,
57
+ event: BookmarkAddedPayload,
50
58
  pinnedActivities: ActivityPinResponse[] | undefined,
51
59
  eventBelongsToCurrentUser: boolean,
52
60
  ) =>
53
61
  updateEntityInArray({
54
62
  entities: pinnedActivities,
55
63
  matcher: (pinnedActivity) =>
56
- pinnedActivity.activity.id === event.bookmark.activity.id,
64
+ pinnedActivity.activity.id === event.bookmark.activity.id &&
65
+ (!eventBelongsToCurrentUser ||
66
+ !pinnedActivity.activity.own_bookmarks.some((b) =>
67
+ isSameBookmark(b, event.bookmark),
68
+ )),
57
69
  updater: (matchedPinnedActivity) => {
58
70
  const newActivity = sharedUpdateActivity({
59
71
  currentActivity: matchedPinnedActivity.activity,
@@ -72,10 +84,7 @@ export const addBookmarkToPinnedActivities = (
72
84
  },
73
85
  });
74
86
 
75
- export function handleBookmarkAdded(
76
- this: Feed,
77
- event: EventPayload<'feeds.bookmark.added'>,
78
- ) {
87
+ export function handleBookmarkAdded(this: Feed, event: BookmarkAddedPayload) {
79
88
  const {
80
89
  activities: currentActivities,
81
90
  pinned_activities: currentPinnedActivities,
@@ -2,14 +2,16 @@ import type { Feed } from '../../../feed';
2
2
  import type {
3
3
  ActivityPinResponse,
4
4
  ActivityResponse,
5
- BookmarkDeletedEvent,
6
5
  BookmarkResponse,
7
6
  } from '../../../gen/models';
8
- import type { EventPayload } from '../../../types-internal';
7
+ import type { EventPayload, PartializeAllBut } from '../../../types-internal';
9
8
  import { updateEntityInArray } from '../../../utils';
10
9
 
11
- // Helper function to check if two bookmarks are the same
12
- // A bookmark is identified by activity_id + folder_id + user_id
10
+ export type BookmarkDeletedPayload = PartializeAllBut<
11
+ EventPayload<'feeds.bookmark.deleted'>,
12
+ 'bookmark'
13
+ >;
14
+
13
15
  export const isSameBookmark = (
14
16
  bookmark1: BookmarkResponse,
15
17
  bookmark2: BookmarkResponse,
@@ -17,7 +19,8 @@ export const isSameBookmark = (
17
19
  return (
18
20
  bookmark1.user.id === bookmark2.user.id &&
19
21
  bookmark1.activity.id === bookmark2.activity.id &&
20
- bookmark1.folder?.id === bookmark2.folder?.id
22
+ bookmark1.folder?.id === bookmark2.folder?.id &&
23
+ bookmark1.updated_at.getTime() === bookmark2.updated_at.getTime()
21
24
  );
22
25
  };
23
26
 
@@ -27,7 +30,7 @@ const sharedUpdateActivity = ({
27
30
  eventBelongsToCurrentUser,
28
31
  }: {
29
32
  currentActivity: ActivityResponse;
30
- event: BookmarkDeletedEvent;
33
+ event: BookmarkDeletedPayload;
31
34
  eventBelongsToCurrentUser: boolean;
32
35
  }): ActivityResponse => {
33
36
  let newOwnBookmarks = currentActivity.own_bookmarks;
@@ -46,13 +49,16 @@ const sharedUpdateActivity = ({
46
49
  };
47
50
 
48
51
  export const removeBookmarkFromActivities = (
49
- event: BookmarkDeletedEvent,
52
+ event: BookmarkDeletedPayload,
50
53
  activities: ActivityResponse[] | undefined,
51
54
  eventBelongsToCurrentUser: boolean,
52
55
  ) =>
53
56
  updateEntityInArray({
54
57
  entities: activities,
55
- matcher: (activity) => activity.id === event.bookmark.activity.id,
58
+ matcher: (activity) =>
59
+ activity.id === event.bookmark.activity.id &&
60
+ (!eventBelongsToCurrentUser ||
61
+ activity.own_bookmarks.some((b) => isSameBookmark(b, event.bookmark))),
56
62
  updater: (matchedActivity) =>
57
63
  sharedUpdateActivity({
58
64
  currentActivity: matchedActivity,
@@ -62,14 +68,18 @@ export const removeBookmarkFromActivities = (
62
68
  });
63
69
 
64
70
  export const removeBookmarkFromPinnedActivities = (
65
- event: BookmarkDeletedEvent,
71
+ event: BookmarkDeletedPayload,
66
72
  pinnedActivities: ActivityPinResponse[] | undefined,
67
73
  eventBelongsToCurrentUser: boolean,
68
74
  ) =>
69
75
  updateEntityInArray({
70
76
  entities: pinnedActivities,
71
77
  matcher: (pinnedActivity) =>
72
- pinnedActivity.activity.id === event.bookmark.activity.id,
78
+ pinnedActivity.activity.id === event.bookmark.activity.id &&
79
+ (!eventBelongsToCurrentUser ||
80
+ pinnedActivity.activity.own_bookmarks.some((b) =>
81
+ isSameBookmark(b, event.bookmark),
82
+ )),
73
83
  updater: (matchedPinnedActivity) => {
74
84
  const newActivity = sharedUpdateActivity({
75
85
  currentActivity: matchedPinnedActivity.activity,
@@ -90,7 +100,7 @@ export const removeBookmarkFromPinnedActivities = (
90
100
 
91
101
  export function handleBookmarkDeleted(
92
102
  this: Feed,
93
- event: EventPayload<'feeds.bookmark.deleted'>,
103
+ event: BookmarkDeletedPayload,
94
104
  ) {
95
105
  const {
96
106
  activities: currentActivities,
@@ -2,27 +2,34 @@ import type { Feed } from '../../../feed';
2
2
  import type {
3
3
  ActivityPinResponse,
4
4
  ActivityResponse,
5
- BookmarkUpdatedEvent,
6
5
  } from '../../../gen/models';
7
- import type { EventPayload } from '../../../types-internal';
6
+ import type { EventPayload, PartializeAllBut } from '../../../types-internal';
8
7
  import { updateEntityInArray } from '../../../utils';
9
8
 
10
9
  import { isSameBookmark } from './handle-bookmark-deleted';
11
10
 
11
+ export type BookmarkUpdatedPayload = PartializeAllBut<
12
+ EventPayload<'feeds.bookmark.updated'>,
13
+ 'bookmark'
14
+ >;
15
+
12
16
  const sharedUpdateActivity = ({
13
17
  currentActivity,
14
18
  event,
15
19
  eventBelongsToCurrentUser,
16
20
  }: {
17
21
  currentActivity: ActivityResponse;
18
- event: BookmarkUpdatedEvent;
22
+ event: BookmarkUpdatedPayload;
19
23
  eventBelongsToCurrentUser: boolean;
20
24
  }): ActivityResponse => {
21
25
  let newOwnBookmarks = currentActivity.own_bookmarks;
22
26
 
23
27
  if (eventBelongsToCurrentUser) {
24
- const bookmarkIndex = newOwnBookmarks.findIndex((bookmark) =>
25
- isSameBookmark(bookmark, event.bookmark),
28
+ const bookmarkIndex = newOwnBookmarks.findIndex(
29
+ (bookmark) =>
30
+ bookmark.user.id === event.bookmark.user.id &&
31
+ bookmark.activity.id === event.bookmark.activity.id &&
32
+ bookmark.folder?.id === event.bookmark.folder?.id,
26
33
  );
27
34
 
28
35
  if (bookmarkIndex !== -1) {
@@ -39,13 +46,16 @@ const sharedUpdateActivity = ({
39
46
  };
40
47
 
41
48
  export const updateBookmarkInActivities = (
42
- event: BookmarkUpdatedEvent,
49
+ event: BookmarkUpdatedPayload,
43
50
  activities: ActivityResponse[] | undefined,
44
51
  eventBelongsToCurrentUser: boolean,
45
52
  ) =>
46
53
  updateEntityInArray({
47
54
  entities: activities,
48
- matcher: (activity) => activity.id === event.bookmark.activity.id,
55
+ matcher: (activity) =>
56
+ activity.id === event.bookmark.activity.id &&
57
+ (!eventBelongsToCurrentUser ||
58
+ !activity.own_bookmarks.some((b) => isSameBookmark(b, event.bookmark))),
49
59
  updater: (matchedActivity) =>
50
60
  sharedUpdateActivity({
51
61
  currentActivity: matchedActivity,
@@ -55,14 +65,18 @@ export const updateBookmarkInActivities = (
55
65
  });
56
66
 
57
67
  export const updateBookmarkInPinnedActivities = (
58
- event: BookmarkUpdatedEvent,
68
+ event: BookmarkUpdatedPayload,
59
69
  pinnedActivities: ActivityPinResponse[] | undefined,
60
70
  eventBelongsToCurrentUser: boolean,
61
71
  ) =>
62
72
  updateEntityInArray({
63
73
  entities: pinnedActivities,
64
74
  matcher: (pinnedActivity) =>
65
- pinnedActivity.activity.id === event.bookmark.activity.id,
75
+ pinnedActivity.activity.id === event.bookmark.activity.id &&
76
+ (!eventBelongsToCurrentUser ||
77
+ !pinnedActivity.activity.own_bookmarks.some((b) =>
78
+ isSameBookmark(b, event.bookmark),
79
+ )),
66
80
  updater: (matchedPinnedActivity) => {
67
81
  const newActivity = sharedUpdateActivity({
68
82
  currentActivity: matchedPinnedActivity.activity,
@@ -83,7 +97,7 @@ export const updateBookmarkInPinnedActivities = (
83
97
 
84
98
  export function handleBookmarkUpdated(
85
99
  this: Feed,
86
- event: EventPayload<'feeds.bookmark.updated'>,
100
+ event: BookmarkUpdatedPayload,
87
101
  ) {
88
102
  const {
89
103
  activities: currentActivities,
@@ -0,0 +1,12 @@
1
+ import type { Feed } from '../../../feed';
2
+ import type { EventPayload } from '../../../types-internal';
3
+
4
+ export function handleFeedDeleted(
5
+ this: Feed,
6
+ event: Pick<EventPayload<'feeds.feed.deleted'>, 'created_at'>,
7
+ ) {
8
+ if (this.currentState.deleted_at) {
9
+ return;
10
+ }
11
+ this.state.partialNext({ deleted_at: event.created_at });
12
+ }
@@ -5,5 +5,13 @@ export function handleFeedUpdated(
5
5
  this: Feed,
6
6
  event: PartializeAllBut<EventPayload<'feeds.feed.updated'>, 'feed'>,
7
7
  ) {
8
+ const currentUpdatedAt = this.currentState.updated_at;
9
+ if (
10
+ currentUpdatedAt &&
11
+ event.feed.updated_at &&
12
+ currentUpdatedAt.getTime() >= event.feed.updated_at.getTime()
13
+ ) {
14
+ return;
15
+ }
8
16
  this.state.partialNext({ ...event.feed });
9
17
  }
@@ -1 +1,2 @@
1
+ export * from './handle-feed-deleted';
1
2
  export * from './handle-feed-updated';
@@ -1,10 +1,33 @@
1
1
  import type { Feed, FeedState } from '../../../feed';
2
- import type { EventPayload } from '../../../types-internal';
2
+ import type { EventPayload, PartializeAllBut } from '../../../types-internal';
3
+ import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
4
+ import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
5
+
6
+ export type FeedMemberAddedPayload = PartializeAllBut<
7
+ EventPayload<'feeds.feed_member.added'>,
8
+ 'member'
9
+ >;
3
10
 
4
11
  export function handleFeedMemberAdded(
5
12
  this: Feed,
6
- event: EventPayload<'feeds.feed_member.added'>,
13
+ event: FeedMemberAddedPayload,
14
+ fromWs?: boolean,
7
15
  ) {
16
+ if (
17
+ !shouldUpdateState({
18
+ stateUpdateQueueId: getStateUpdateQueueId(event, 'feed-member-added'),
19
+ stateUpdateQueue: this.stateUpdateQueue,
20
+ watch: this.currentState.watch,
21
+ fromWs,
22
+ isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(
23
+ this,
24
+ event,
25
+ ),
26
+ })
27
+ ) {
28
+ return;
29
+ }
30
+
8
31
  const { connected_user: connectedUser } = this.client.state.getLatestValue();
9
32
 
10
33
  this.state.next((currentState) => {
@@ -1,10 +1,33 @@
1
1
  import type { Feed, FeedState } from '../../../feed';
2
- import type { EventPayload } from '../../../types-internal';
2
+ import type { EventPayload, PartializeAllBut } from '../../../types-internal';
3
+ import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
4
+ import { eventTriggeredByConnectedUser } from '../../../utils/event-triggered-by-connected-user';
5
+
6
+ export type FeedMemberRemovedPayload = PartializeAllBut<
7
+ EventPayload<'feeds.feed_member.removed'>,
8
+ 'member_id'
9
+ >;
3
10
 
4
11
  export function handleFeedMemberRemoved(
5
12
  this: Feed,
6
- event: EventPayload<'feeds.feed_member.removed'>,
13
+ event: FeedMemberRemovedPayload,
14
+ fromWs?: boolean,
7
15
  ) {
16
+ if (
17
+ !shouldUpdateState({
18
+ stateUpdateQueueId: getStateUpdateQueueId(event, 'feed-member-removed'),
19
+ stateUpdateQueue: this.stateUpdateQueue,
20
+ watch: this.currentState.watch,
21
+ fromWs,
22
+ isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(
23
+ this,
24
+ event,
25
+ ),
26
+ })
27
+ ) {
28
+ return;
29
+ }
30
+
8
31
  const { connected_user: connectedUser } = this.client.state.getLatestValue();
9
32
 
10
33
  this.state.next((currentState) => {