@liveblocks/react 1.10.0-beta2 → 1.10.0-beta4

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/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { detectDupes } from "@liveblocks/core";
5
5
 
6
6
  // src/version.ts
7
7
  var PKG_NAME = "@liveblocks/react";
8
- var PKG_VERSION = "1.10.0-beta2";
8
+ var PKG_VERSION = "1.10.0-beta4";
9
9
  var PKG_FORMAT = "esm";
10
10
 
11
11
  // src/ClientSideSuspense.tsx
@@ -34,7 +34,10 @@ import { useSyncExternalStoreWithSelector as useSyncExternalStoreWithSelector2 }
34
34
  import { applyOptimisticUpdates } from "@liveblocks/core";
35
35
  function selectedInboxNotifications(state) {
36
36
  const result = applyOptimisticUpdates(state);
37
- return Object.values(result.inboxNotifications);
37
+ return Object.values(result.inboxNotifications).sort(
38
+ // Sort so that the most recent notifications are first
39
+ (a, b) => b.notifiedAt.getTime() - a.notifiedAt.getTime()
40
+ );
38
41
  }
39
42
 
40
43
  // src/shared.ts
@@ -45,8 +48,10 @@ import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external
45
48
  // src/room.tsx
46
49
  import { shallow } from "@liveblocks/client";
47
50
  import {
51
+ addReaction,
48
52
  CommentsApiError,
49
53
  console as console2,
54
+ deleteComment,
50
55
  deprecateIf,
51
56
  errorIf,
52
57
  isLiveNode,
@@ -54,8 +59,10 @@ import {
54
59
  makeEventSource,
55
60
  makePoller,
56
61
  NotificationsApiError,
62
+ removeReaction,
57
63
  ServerMsgCode,
58
- stringify
64
+ stringify,
65
+ upsertComment
59
66
  } from "@liveblocks/core";
60
67
  import { nanoid as nanoid2 } from "nanoid";
61
68
  import * as React2 from "react";
@@ -151,11 +158,12 @@ function createCommentId() {
151
158
 
152
159
  // src/comments/lib/select-notification-settings.ts
153
160
  import {
154
- applyOptimisticUpdates as applyOptimisticUpdates2
161
+ applyOptimisticUpdates as applyOptimisticUpdates2,
162
+ nn
155
163
  } from "@liveblocks/core";
156
164
  function selectNotificationSettings(roomId, state) {
157
165
  const { notificationSettings } = applyOptimisticUpdates2(state);
158
- return notificationSettings[roomId];
166
+ return nn(notificationSettings[roomId]);
159
167
  }
160
168
 
161
169
  // src/comments/lib/selected-threads.ts
@@ -167,6 +175,9 @@ function selectedThreads(roomId, state, options) {
167
175
  const threads = Object.values(result.threads).filter((thread) => {
168
176
  if (thread.roomId !== roomId)
169
177
  return false;
178
+ if (thread.deletedAt !== void 0) {
179
+ return false;
180
+ }
170
181
  const query = options.query;
171
182
  if (!query)
172
183
  return true;
@@ -180,34 +191,6 @@ function selectedThreads(roomId, state, options) {
180
191
  return threads.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
181
192
  }
182
193
 
183
- // src/comments/lib/upsert-comment.ts
184
- function upsertComment(threads, newComment) {
185
- const thread = threads[newComment.threadId];
186
- if (thread === void 0) {
187
- return threads;
188
- }
189
- const newComments = [];
190
- let updated = false;
191
- for (const comment of thread.comments) {
192
- if (comment.id === newComment.id) {
193
- updated = true;
194
- newComments.push(newComment);
195
- } else {
196
- newComments.push(comment);
197
- }
198
- }
199
- if (!updated) {
200
- newComments.push(newComment);
201
- }
202
- return {
203
- ...threads,
204
- [thread.id]: {
205
- ...thread,
206
- comments: newComments
207
- }
208
- };
209
- }
210
-
211
194
  // src/lib/use-initial.ts
212
195
  import { useState as useState2 } from "react";
213
196
  function useInitial(value) {
@@ -376,7 +359,9 @@ function createRoomContext(client, options) {
376
359
  );
377
360
  React2.useEffect(() => {
378
361
  async function handleCommentEvent(message) {
379
- const info = await room.getThread({ threadId: message.threadId });
362
+ const info = await room[kInternal].comments.getThread({
363
+ threadId: message.threadId
364
+ });
380
365
  if (!info) {
381
366
  store.deleteThread(message.threadId);
382
367
  return;
@@ -404,6 +389,9 @@ function createRoomContext(client, options) {
404
389
  (message) => void handleCommentEvent(message)
405
390
  );
406
391
  }, [room]);
392
+ React2.useEffect(() => {
393
+ void getThreadsUpdates(room.id);
394
+ }, [room.id]);
407
395
  React2.useEffect(() => {
408
396
  const pair = stableEnterRoom(roomId, frozenProps);
409
397
  setRoomLeavePair(pair);
@@ -786,15 +774,46 @@ function createRoomContext(client, options) {
786
774
  const requestsCache = /* @__PURE__ */ new Map();
787
775
  const poller = makePoller(refreshThreadsAndNotifications);
788
776
  async function refreshThreadsAndNotifications() {
789
- await Promise.allSettled(
790
- Array.from(requestsCache.entries()).filter(([_, requestCache]) => requestCache.subscribers > 0).map(async ([_, requestCache]) => {
791
- return requestCache.requestFactory().then(
792
- (result) => requestCache.onSuccess(result),
793
- () => {
794
- }
777
+ const requests = [];
778
+ client[kInternal].getRoomIds().map((roomId) => {
779
+ const room = client.getRoom(roomId);
780
+ if (room === null)
781
+ return;
782
+ const notificationSettingsQuery = makeNotificationSettingsQueryKey(
783
+ room.id
784
+ );
785
+ if (requestsCache.has(notificationSettingsQuery)) {
786
+ requests.push(
787
+ room[kInternal].notifications.getRoomNotificationSettings().then((settings) => {
788
+ store.updateRoomInboxNotificationSettings(
789
+ room.id,
790
+ settings,
791
+ notificationSettingsQuery
792
+ );
793
+ }).catch(() => {
794
+ })
795
795
  );
796
- })
797
- );
796
+ }
797
+ const lastRequestedAt = lastRequestedAtByRoom.get(room.id);
798
+ if (lastRequestedAt === void 0)
799
+ return;
800
+ requests.push(
801
+ room[kInternal].comments.getThreads({ since: lastRequestedAt }).then((result) => {
802
+ store.updateThreadsAndNotifications(
803
+ result.threads,
804
+ result.inboxNotifications,
805
+ result.deletedThreads,
806
+ result.deletedInboxNotifications
807
+ );
808
+ const room2 = client.getRoom(roomId);
809
+ if (room2 === null)
810
+ return;
811
+ lastRequestedAtByRoom.set(room2.id, result.meta.requestedAt);
812
+ }).catch(() => {
813
+ })
814
+ );
815
+ });
816
+ await Promise.allSettled(requests);
798
817
  }
799
818
  function incrementQuerySubscribers(queryKey) {
800
819
  const requestCache = requestsCache.get(queryKey);
@@ -824,16 +843,14 @@ function createRoomContext(client, options) {
824
843
  poller.stop();
825
844
  }
826
845
  }
827
- async function getOrInitRequest(queryKey, requestFactory, onSuccess) {
846
+ async function getThreadsAndInboxNotifications(room, queryKey, options2) {
828
847
  const requestInfo = requestsCache.get(queryKey);
829
848
  if (requestInfo !== void 0) {
830
849
  return requestInfo.promise;
831
850
  }
832
- const promise = requestFactory();
851
+ const promise = room[kInternal].comments.getThreads(options2);
833
852
  requestsCache.set(queryKey, {
834
853
  promise,
835
- requestFactory,
836
- onSuccess,
837
854
  subscribers: 0
838
855
  });
839
856
  store.setQueryState(queryKey, {
@@ -841,36 +858,53 @@ function createRoomContext(client, options) {
841
858
  });
842
859
  try {
843
860
  const result = await promise;
844
- onSuccess(result);
845
- } catch (er) {
861
+ store.updateThreadsAndNotifications(
862
+ result.threads,
863
+ result.inboxNotifications,
864
+ result.deletedThreads,
865
+ result.deletedInboxNotifications,
866
+ queryKey
867
+ );
868
+ const lastRequestedAt = lastRequestedAtByRoom.get(room.id);
869
+ if (lastRequestedAt === void 0 || lastRequestedAt > result.meta.requestedAt) {
870
+ lastRequestedAtByRoom.set(room.id, result.meta.requestedAt);
871
+ }
872
+ } catch (err) {
846
873
  store.setQueryState(queryKey, {
847
874
  isLoading: false,
848
- error: er
875
+ error: err
849
876
  });
850
877
  }
851
878
  poller.start(POLLING_INTERVAL);
852
879
  }
853
- async function getThreadsAndInboxNotifications(room, queryKey, options2) {
854
- const roomId = room.id;
855
- return getOrInitRequest(
856
- queryKey,
857
- async () => {
858
- const room2 = client.getRoom(roomId);
859
- if (room2 === null) {
860
- return;
861
- }
862
- return room2.getThreads(options2);
863
- },
864
- (result) => {
865
- if (result !== void 0) {
866
- store.updateThreadsAndNotifications(
867
- result.threads,
868
- result.inboxNotifications,
869
- queryKey
870
- );
871
- }
872
- }
873
- );
880
+ const DEFAULT_DEDUPING_INTERVAL = 2e3;
881
+ const lastRequestedAtByRoom = /* @__PURE__ */ new Map();
882
+ let isFetchingThreadsUpdates = false;
883
+ async function getThreadsUpdates(roomId) {
884
+ const room = client.getRoom(roomId);
885
+ if (room === null)
886
+ return;
887
+ const since = lastRequestedAtByRoom.get(room.id);
888
+ if (since === void 0)
889
+ return;
890
+ if (isFetchingThreadsUpdates)
891
+ return;
892
+ try {
893
+ isFetchingThreadsUpdates = true;
894
+ const updates = await room[kInternal].comments.getThreads({ since });
895
+ setTimeout(() => {
896
+ isFetchingThreadsUpdates = false;
897
+ }, DEFAULT_DEDUPING_INTERVAL);
898
+ store.updateThreadsAndNotifications(
899
+ updates.threads,
900
+ updates.inboxNotifications,
901
+ updates.deletedThreads,
902
+ updates.deletedInboxNotifications
903
+ );
904
+ lastRequestedAtByRoom.set(room.id, updates.meta.requestedAt);
905
+ } catch (err) {
906
+ isFetchingThreadsUpdates = false;
907
+ }
874
908
  }
875
909
  function useThreads(options2 = { query: { metadata: {} } }) {
876
910
  const room = useRoom();
@@ -885,7 +919,8 @@ function createRoomContext(client, options) {
885
919
  }, [room, queryKey]);
886
920
  const selector = React2.useCallback(
887
921
  (state) => {
888
- if (state.queries[queryKey] === void 0 || state.queries[queryKey].isLoading) {
922
+ const query = state.queries[queryKey];
923
+ if (query === void 0 || query.isLoading) {
889
924
  return {
890
925
  isLoading: true
891
926
  };
@@ -893,7 +928,7 @@ function createRoomContext(client, options) {
893
928
  return {
894
929
  threads: selectedThreads(room.id, state, options2),
895
930
  isLoading: false,
896
- error: state.queries[queryKey].error
931
+ error: query.error
897
932
  };
898
933
  },
899
934
  [room, queryKey]
@@ -934,7 +969,7 @@ function createRoomContext(client, options) {
934
969
  return () => {
935
970
  decrementQuerySubscribers(queryKey);
936
971
  };
937
- }, [room, queryKey]);
972
+ }, [queryKey]);
938
973
  return useSyncExternalStoreWithSelector(
939
974
  store.subscribe,
940
975
  store.get,
@@ -950,12 +985,12 @@ function createRoomContext(client, options) {
950
985
  const metadata = "metadata" in options2 ? options2.metadata : {};
951
986
  const threadId = createThreadId();
952
987
  const commentId = createCommentId();
953
- const now = /* @__PURE__ */ new Date();
988
+ const createdAt = /* @__PURE__ */ new Date();
954
989
  const newComment = {
955
990
  id: commentId,
956
991
  threadId,
957
992
  roomId: room.id,
958
- createdAt: now,
993
+ createdAt,
959
994
  type: "comment",
960
995
  userId: getCurrentUserId(room),
961
996
  body,
@@ -964,7 +999,8 @@ function createRoomContext(client, options) {
964
999
  const newThread = {
965
1000
  id: threadId,
966
1001
  type: "thread",
967
- createdAt: now,
1002
+ createdAt,
1003
+ updatedAt: createdAt,
968
1004
  roomId: room.id,
969
1005
  metadata,
970
1006
  comments: [newComment]
@@ -975,7 +1011,7 @@ function createRoomContext(client, options) {
975
1011
  thread: newThread,
976
1012
  id: optimisticUpdateId
977
1013
  });
978
- room.createThread({ threadId, commentId, body, metadata }).then(
1014
+ room[kInternal].comments.createThread({ threadId, commentId, body, metadata }).then(
979
1015
  (thread) => {
980
1016
  store.set((state) => ({
981
1017
  ...state,
@@ -1014,31 +1050,55 @@ function createRoomContext(client, options) {
1014
1050
  }
1015
1051
  const threadId = options2.threadId;
1016
1052
  const metadata = options2.metadata;
1053
+ const updatedAt = /* @__PURE__ */ new Date();
1017
1054
  const optimisticUpdateId = nanoid2();
1018
1055
  store.pushOptimisticUpdate({
1019
1056
  type: "edit-thread-metadata",
1020
1057
  metadata,
1021
1058
  id: optimisticUpdateId,
1022
- threadId
1059
+ threadId,
1060
+ updatedAt
1023
1061
  });
1024
- room.editThreadMetadata({ metadata, threadId }).then(
1062
+ room[kInternal].comments.editThreadMetadata({ metadata, threadId }).then(
1025
1063
  (metadata2) => {
1026
- store.set((state) => ({
1027
- ...state,
1028
- threads: {
1029
- ...state.threads,
1030
- [threadId]: {
1031
- ...state.threads[threadId],
1032
- metadata: {
1033
- ...state.threads[threadId].metadata,
1034
- ...metadata2
1035
- }
1036
- }
1037
- },
1038
- optimisticUpdates: state.optimisticUpdates.filter(
1064
+ store.set((state) => {
1065
+ const existingThread = state.threads[threadId];
1066
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1039
1067
  (update) => update.id !== optimisticUpdateId
1040
- )
1041
- }));
1068
+ );
1069
+ if (existingThread === void 0) {
1070
+ return {
1071
+ ...state,
1072
+ optimisticUpdates: updatedOptimisticUpdates
1073
+ };
1074
+ }
1075
+ if (existingThread.deletedAt !== void 0) {
1076
+ return {
1077
+ ...state,
1078
+ optimisticUpdates: updatedOptimisticUpdates
1079
+ };
1080
+ }
1081
+ if (existingThread.updatedAt && existingThread.updatedAt > updatedAt) {
1082
+ return {
1083
+ ...state,
1084
+ optimisticUpdates: updatedOptimisticUpdates
1085
+ };
1086
+ }
1087
+ return {
1088
+ ...state,
1089
+ threads: {
1090
+ ...state.threads,
1091
+ [threadId]: {
1092
+ ...existingThread,
1093
+ metadata: {
1094
+ ...existingThread.metadata,
1095
+ ...metadata2
1096
+ }
1097
+ }
1098
+ },
1099
+ optimisticUpdates: updatedOptimisticUpdates
1100
+ };
1101
+ });
1042
1102
  },
1043
1103
  (err) => onMutationFailure(
1044
1104
  err,
@@ -1058,55 +1118,46 @@ function createRoomContext(client, options) {
1058
1118
  const room = useRoom();
1059
1119
  return React2.useCallback(
1060
1120
  ({ threadId, commentId, emoji }) => {
1061
- const now = /* @__PURE__ */ new Date();
1121
+ const createdAt = /* @__PURE__ */ new Date();
1062
1122
  const userId = getCurrentUserId(room);
1063
1123
  const optimisticUpdateId = nanoid2();
1064
1124
  store.pushOptimisticUpdate({
1065
1125
  type: "add-reaction",
1066
1126
  threadId,
1067
1127
  commentId,
1068
- emoji,
1069
- userId,
1070
- createdAt: now,
1128
+ reaction: {
1129
+ emoji,
1130
+ userId,
1131
+ createdAt
1132
+ },
1071
1133
  id: optimisticUpdateId
1072
1134
  });
1073
- room.addReaction({ threadId, commentId, emoji }).then(
1135
+ room[kInternal].comments.addReaction({ threadId, commentId, emoji }).then(
1074
1136
  (addedReaction) => {
1075
- store.set((state) => ({
1076
- ...state,
1077
- threads: {
1078
- ...state.threads,
1079
- [threadId]: {
1080
- ...state.threads[threadId],
1081
- comments: state.threads[threadId].comments.map(
1082
- (comment) => comment.id === commentId ? {
1083
- ...comment,
1084
- reactions: comment.reactions.some(
1085
- (reaction) => reaction.emoji === addedReaction.emoji
1086
- ) ? comment.reactions.map(
1087
- (reaction) => reaction.emoji === addedReaction.emoji ? {
1088
- ...reaction,
1089
- users: [
1090
- ...reaction.users,
1091
- { id: addedReaction.userId }
1092
- ]
1093
- } : reaction
1094
- ) : [
1095
- ...comment.reactions,
1096
- {
1097
- emoji: addedReaction.emoji,
1098
- createdAt: addedReaction.createdAt,
1099
- users: [{ id: addedReaction.userId }]
1100
- }
1101
- ]
1102
- } : comment
1103
- )
1104
- }
1105
- },
1106
- optimisticUpdates: state.optimisticUpdates.filter(
1137
+ store.set((state) => {
1138
+ const existingThread = state.threads[threadId];
1139
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1107
1140
  (update) => update.id !== optimisticUpdateId
1108
- )
1109
- }));
1141
+ );
1142
+ if (existingThread === void 0) {
1143
+ return {
1144
+ ...state,
1145
+ optimisticUpdates: updatedOptimisticUpdates
1146
+ };
1147
+ }
1148
+ return {
1149
+ ...state,
1150
+ threads: {
1151
+ ...state.threads,
1152
+ [threadId]: addReaction(
1153
+ existingThread,
1154
+ commentId,
1155
+ addedReaction
1156
+ )
1157
+ },
1158
+ optimisticUpdates: updatedOptimisticUpdates
1159
+ };
1160
+ });
1110
1161
  },
1111
1162
  (err) => onMutationFailure(
1112
1163
  err,
@@ -1128,6 +1179,7 @@ function createRoomContext(client, options) {
1128
1179
  return React2.useCallback(
1129
1180
  ({ threadId, commentId, emoji }) => {
1130
1181
  const userId = getCurrentUserId(room);
1182
+ const removedAt = /* @__PURE__ */ new Date();
1131
1183
  const optimisticUpdateId = nanoid2();
1132
1184
  store.pushOptimisticUpdate({
1133
1185
  type: "remove-reaction",
@@ -1135,50 +1187,37 @@ function createRoomContext(client, options) {
1135
1187
  commentId,
1136
1188
  emoji,
1137
1189
  userId,
1190
+ removedAt,
1138
1191
  id: optimisticUpdateId
1139
1192
  });
1140
- room.removeReaction({ threadId, commentId, emoji }).then(
1193
+ room[kInternal].comments.removeReaction({ threadId, commentId, emoji }).then(
1141
1194
  () => {
1142
- store.set((state) => ({
1143
- ...state,
1144
- threads: {
1145
- ...state.threads,
1146
- [threadId]: {
1147
- ...state.threads[threadId],
1148
- comments: state.threads[threadId].comments.map((comment) => {
1149
- if (comment.id !== commentId) {
1150
- return comment;
1151
- }
1152
- const reactionIndex = comment.reactions.findIndex(
1153
- (reaction) => reaction.emoji === emoji
1154
- );
1155
- let reactions = comment.reactions;
1156
- if (reactionIndex >= 0 && comment.reactions[reactionIndex].users.some(
1157
- (user) => user.id === userId
1158
- )) {
1159
- if (comment.reactions[reactionIndex].users.length <= 1) {
1160
- reactions = [...comment.reactions];
1161
- reactions.splice(reactionIndex, 1);
1162
- } else {
1163
- reactions[reactionIndex] = {
1164
- ...reactions[reactionIndex],
1165
- users: reactions[reactionIndex].users.filter(
1166
- (user) => user.id !== userId
1167
- )
1168
- };
1169
- }
1170
- }
1171
- return {
1172
- ...comment,
1173
- reactions
1174
- };
1175
- })
1176
- }
1177
- },
1178
- optimisticUpdates: state.optimisticUpdates.filter(
1195
+ store.set((state) => {
1196
+ const existingThread = state.threads[threadId];
1197
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1179
1198
  (update) => update.id !== optimisticUpdateId
1180
- )
1181
- }));
1199
+ );
1200
+ if (existingThread === void 0) {
1201
+ return {
1202
+ ...state,
1203
+ optimisticUpdates: updatedOptimisticUpdates
1204
+ };
1205
+ }
1206
+ return {
1207
+ ...state,
1208
+ threads: {
1209
+ ...state.threads,
1210
+ [threadId]: removeReaction(
1211
+ existingThread,
1212
+ commentId,
1213
+ emoji,
1214
+ userId,
1215
+ removedAt
1216
+ )
1217
+ },
1218
+ optimisticUpdates: updatedOptimisticUpdates
1219
+ };
1220
+ });
1182
1221
  },
1183
1222
  (err) => onMutationFailure(
1184
1223
  err,
@@ -1200,44 +1239,58 @@ function createRoomContext(client, options) {
1200
1239
  return React2.useCallback(
1201
1240
  ({ threadId, body }) => {
1202
1241
  const commentId = createCommentId();
1203
- const now = /* @__PURE__ */ new Date();
1242
+ const createdAt = /* @__PURE__ */ new Date();
1204
1243
  const comment = {
1205
1244
  id: commentId,
1206
1245
  threadId,
1207
1246
  roomId: room.id,
1208
1247
  type: "comment",
1209
- createdAt: now,
1248
+ createdAt,
1210
1249
  userId: getCurrentUserId(room),
1211
1250
  body,
1212
1251
  reactions: []
1213
1252
  };
1214
1253
  const optimisticUpdateId = nanoid2();
1215
- const inboxNotification = Object.values(
1216
- store.get().inboxNotifications
1217
- ).find((inboxNotification2) => inboxNotification2.threadId === threadId);
1218
1254
  store.pushOptimisticUpdate({
1219
1255
  type: "create-comment",
1220
1256
  comment,
1221
- id: optimisticUpdateId,
1222
- inboxNotificationId: inboxNotification?.id
1257
+ id: optimisticUpdateId
1223
1258
  });
1224
- room.createComment({ threadId, commentId, body }).then(
1259
+ room[kInternal].comments.createComment({ threadId, commentId, body }).then(
1225
1260
  (newComment) => {
1226
- store.set((state) => ({
1227
- ...state,
1228
- threads: upsertComment(state.threads, newComment),
1229
- inboxNotifications: inboxNotification ? {
1261
+ store.set((state) => {
1262
+ const existingThread = state.threads[threadId];
1263
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1264
+ (update) => update.id !== optimisticUpdateId
1265
+ );
1266
+ if (existingThread === void 0) {
1267
+ return {
1268
+ ...state,
1269
+ optimisticUpdates: updatedOptimisticUpdates
1270
+ };
1271
+ }
1272
+ const inboxNotification = Object.values(
1273
+ state.inboxNotifications
1274
+ ).find((notification) => notification.threadId === threadId);
1275
+ const updatedInboxNotifications = inboxNotification !== void 0 ? {
1230
1276
  ...state.inboxNotifications,
1231
1277
  [inboxNotification.id]: {
1232
1278
  ...inboxNotification,
1233
1279
  notifiedAt: newComment.createdAt,
1234
1280
  readAt: newComment.createdAt
1235
1281
  }
1236
- } : state.inboxNotifications,
1237
- optimisticUpdates: state.optimisticUpdates.filter(
1238
- (update) => update.id !== optimisticUpdateId
1239
- )
1240
- }));
1282
+ } : state.inboxNotifications;
1283
+ return {
1284
+ ...state,
1285
+ threads: {
1286
+ ...state.threads,
1287
+ [threadId]: upsertComment(existingThread, newComment)
1288
+ // Upsert the new comment into the thread comments list (if applicable)
1289
+ },
1290
+ inboxNotifications: updatedInboxNotifications,
1291
+ optimisticUpdates: updatedOptimisticUpdates
1292
+ };
1293
+ });
1241
1294
  },
1242
1295
  (err) => onMutationFailure(
1243
1296
  err,
@@ -1259,25 +1312,56 @@ function createRoomContext(client, options) {
1259
1312
  const room = useRoom();
1260
1313
  return React2.useCallback(
1261
1314
  ({ threadId, commentId, body }) => {
1262
- const now = /* @__PURE__ */ new Date();
1315
+ const editedAt = /* @__PURE__ */ new Date();
1263
1316
  const optimisticUpdateId = nanoid2();
1317
+ const thread = store.get().threads[threadId];
1318
+ if (thread === void 0) {
1319
+ console2.warn(
1320
+ `Internal unexpected behavior. Cannot edit comment in thread "${threadId}" because the thread does not exist in the cache.`
1321
+ );
1322
+ return;
1323
+ }
1324
+ const comment = thread.comments.find(
1325
+ (comment2) => comment2.id === commentId
1326
+ );
1327
+ if (comment === void 0 || comment.deletedAt !== void 0) {
1328
+ console2.warn(
1329
+ `Internal unexpected behavior. Cannot edit comment "${commentId}" in thread "${threadId}" because the comment does not exist in the cache.`
1330
+ );
1331
+ return;
1332
+ }
1264
1333
  store.pushOptimisticUpdate({
1265
1334
  type: "edit-comment",
1266
- threadId,
1267
- commentId,
1268
- body,
1269
- editedAt: now,
1335
+ comment: {
1336
+ ...comment,
1337
+ editedAt,
1338
+ body
1339
+ },
1270
1340
  id: optimisticUpdateId
1271
1341
  });
1272
- room.editComment({ threadId, commentId, body }).then(
1342
+ room[kInternal].comments.editComment({ threadId, commentId, body }).then(
1273
1343
  (editedComment) => {
1274
- store.set((state) => ({
1275
- ...state,
1276
- threads: upsertComment(state.threads, editedComment),
1277
- optimisticUpdates: state.optimisticUpdates.filter(
1344
+ store.set((state) => {
1345
+ const existingThread = state.threads[threadId];
1346
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1278
1347
  (update) => update.id !== optimisticUpdateId
1279
- )
1280
- }));
1348
+ );
1349
+ if (existingThread === void 0) {
1350
+ return {
1351
+ ...state,
1352
+ optimisticUpdates: updatedOptimisticUpdates
1353
+ };
1354
+ }
1355
+ return {
1356
+ ...state,
1357
+ threads: {
1358
+ ...state.threads,
1359
+ [threadId]: upsertComment(existingThread, editedComment)
1360
+ // Upsert the edited comment into the thread comments list (if applicable)
1361
+ },
1362
+ optimisticUpdates: updatedOptimisticUpdates
1363
+ };
1364
+ });
1281
1365
  },
1282
1366
  (err) => onMutationFailure(
1283
1367
  err,
@@ -1298,43 +1382,41 @@ function createRoomContext(client, options) {
1298
1382
  const room = useRoom();
1299
1383
  return React2.useCallback(
1300
1384
  ({ threadId, commentId }) => {
1301
- const now = /* @__PURE__ */ new Date();
1385
+ const deletedAt = /* @__PURE__ */ new Date();
1302
1386
  const optimisticUpdateId = nanoid2();
1303
1387
  store.pushOptimisticUpdate({
1304
1388
  type: "delete-comment",
1305
1389
  threadId,
1306
1390
  commentId,
1307
- deletedAt: now,
1391
+ deletedAt,
1308
1392
  id: optimisticUpdateId
1309
1393
  });
1310
- room.deleteComment({ threadId, commentId }).then(
1394
+ room[kInternal].comments.deleteComment({ threadId, commentId }).then(
1311
1395
  () => {
1312
- const newThreads = { ...store.get().threads };
1313
- const thread = newThreads[threadId];
1314
- if (thread === void 0)
1315
- return;
1316
- newThreads[thread.id] = {
1317
- ...thread,
1318
- comments: thread.comments.map(
1319
- (comment) => comment.id === commentId ? {
1320
- ...comment,
1321
- deletedAt: now,
1322
- body: void 0
1323
- } : comment
1324
- )
1325
- };
1326
- if (!newThreads[thread.id].comments.some(
1327
- (comment) => comment.deletedAt === void 0
1328
- )) {
1329
- delete newThreads[thread.id];
1330
- }
1331
- store.set((state) => ({
1332
- ...state,
1333
- threads: newThreads,
1334
- optimisticUpdates: state.optimisticUpdates.filter(
1396
+ store.set((state) => {
1397
+ const existingThread = state.threads[threadId];
1398
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1335
1399
  (update) => update.id !== optimisticUpdateId
1336
- )
1337
- }));
1400
+ );
1401
+ if (existingThread === void 0) {
1402
+ return {
1403
+ ...state,
1404
+ optimisticUpdates: updatedOptimisticUpdates
1405
+ };
1406
+ }
1407
+ return {
1408
+ ...state,
1409
+ threads: {
1410
+ ...state.threads,
1411
+ [threadId]: deleteComment(
1412
+ existingThread,
1413
+ commentId,
1414
+ deletedAt
1415
+ )
1416
+ },
1417
+ optimisticUpdates: updatedOptimisticUpdates
1418
+ };
1419
+ });
1338
1420
  },
1339
1421
  (err) => onMutationFailure(
1340
1422
  err,
@@ -1402,10 +1484,7 @@ function createRoomContext(client, options) {
1402
1484
  return mentionSuggestions;
1403
1485
  }
1404
1486
  function useThreadSubscription(threadId) {
1405
- return useSyncExternalStoreWithSelector(
1406
- store.subscribe,
1407
- store.get,
1408
- store.get,
1487
+ const selector = React2.useCallback(
1409
1488
  (state) => {
1410
1489
  const inboxNotification = selectedInboxNotifications(state).find(
1411
1490
  (inboxNotification2) => inboxNotification2.threadId === threadId
@@ -1420,85 +1499,90 @@ function createRoomContext(client, options) {
1420
1499
  status: "subscribed",
1421
1500
  unreadSince: inboxNotification.readAt
1422
1501
  };
1423
- }
1502
+ },
1503
+ [threadId]
1504
+ );
1505
+ return useSyncExternalStoreWithSelector(
1506
+ store.subscribe,
1507
+ store.get,
1508
+ store.get,
1509
+ selector
1424
1510
  );
1425
1511
  }
1426
1512
  function useMarkThreadAsRead() {
1427
- return React2.useCallback((threadId) => {
1428
- const inboxNotification = Object.values(
1429
- store.get().inboxNotifications
1430
- ).find((inboxNotification2) => inboxNotification2.threadId === threadId);
1431
- if (!inboxNotification)
1432
- return;
1433
- const optimisticUpdateId = nanoid2();
1434
- const now = /* @__PURE__ */ new Date();
1435
- store.pushOptimisticUpdate({
1436
- type: "mark-inbox-notification-as-read",
1437
- id: optimisticUpdateId,
1438
- inboxNotificationId: inboxNotification.id,
1439
- readAt: now
1440
- });
1441
- client.markInboxNotificationAsRead(inboxNotification.id).then(
1442
- () => {
1443
- store.set((state) => ({
1444
- ...state,
1445
- inboxNotifications: {
1446
- ...state.inboxNotifications,
1447
- [inboxNotification.id]: {
1448
- ...inboxNotification,
1449
- readAt: now
1450
- }
1451
- },
1452
- optimisticUpdates: state.optimisticUpdates.filter(
1453
- (update) => update.id !== optimisticUpdateId
1454
- )
1455
- }));
1456
- },
1457
- (err) => {
1458
- onMutationFailure(
1459
- err,
1460
- optimisticUpdateId,
1461
- (error) => new MarkInboxNotificationAsReadError(error, {
1462
- inboxNotificationId: inboxNotification.id
1463
- })
1464
- );
1513
+ const room = useRoom();
1514
+ return React2.useCallback(
1515
+ (threadId) => {
1516
+ const inboxNotification = Object.values(
1517
+ store.get().inboxNotifications
1518
+ ).find((inboxNotification2) => inboxNotification2.threadId === threadId);
1519
+ if (!inboxNotification)
1465
1520
  return;
1466
- }
1467
- );
1468
- }, []);
1521
+ const optimisticUpdateId = nanoid2();
1522
+ const now = /* @__PURE__ */ new Date();
1523
+ store.pushOptimisticUpdate({
1524
+ type: "mark-inbox-notification-as-read",
1525
+ id: optimisticUpdateId,
1526
+ inboxNotificationId: inboxNotification.id,
1527
+ readAt: now
1528
+ });
1529
+ room[kInternal].notifications.markInboxNotificationAsRead(inboxNotification.id).then(
1530
+ () => {
1531
+ store.set((state) => ({
1532
+ ...state,
1533
+ inboxNotifications: {
1534
+ ...state.inboxNotifications,
1535
+ [inboxNotification.id]: {
1536
+ ...inboxNotification,
1537
+ readAt: now
1538
+ }
1539
+ },
1540
+ optimisticUpdates: state.optimisticUpdates.filter(
1541
+ (update) => update.id !== optimisticUpdateId
1542
+ )
1543
+ }));
1544
+ },
1545
+ (err) => {
1546
+ onMutationFailure(
1547
+ err,
1548
+ optimisticUpdateId,
1549
+ (error) => new MarkInboxNotificationAsReadError(error, {
1550
+ inboxNotificationId: inboxNotification.id
1551
+ })
1552
+ );
1553
+ return;
1554
+ }
1555
+ );
1556
+ },
1557
+ [room]
1558
+ );
1469
1559
  }
1470
1560
  function makeNotificationSettingsQueryKey(roomId) {
1471
1561
  return `${roomId}:NOTIFICATION_SETTINGS`;
1472
1562
  }
1473
1563
  async function getInboxNotificationSettings(room, queryKey) {
1474
- const roomId = room.id;
1475
- return getOrInitRequest(
1476
- queryKey,
1477
- async () => {
1478
- const room2 = client.getRoom(roomId);
1479
- if (room2 === null) {
1480
- return;
1481
- }
1482
- return room2.getRoomNotificationSettings();
1483
- },
1484
- (settings) => {
1485
- if (settings !== void 0) {
1486
- store.set((state) => ({
1487
- ...state,
1488
- notificationSettings: {
1489
- ...state.notificationSettings,
1490
- [room.id]: settings
1491
- },
1492
- queries: {
1493
- ...state.queries,
1494
- [queryKey]: {
1495
- isLoading: false
1496
- }
1497
- }
1498
- }));
1499
- }
1500
- }
1501
- );
1564
+ const requestInfo = requestsCache.get(queryKey);
1565
+ if (requestInfo !== void 0) {
1566
+ return requestInfo.promise;
1567
+ }
1568
+ const promise = room[kInternal].notifications.getRoomNotificationSettings();
1569
+ requestsCache.set(queryKey, {
1570
+ promise,
1571
+ subscribers: 0
1572
+ });
1573
+ store.setQueryState(queryKey, {
1574
+ isLoading: true
1575
+ });
1576
+ try {
1577
+ const settings = await promise;
1578
+ store.updateRoomInboxNotificationSettings(room.id, settings, queryKey);
1579
+ } catch (err) {
1580
+ store.setQueryState(queryKey, {
1581
+ isLoading: false,
1582
+ error: err
1583
+ });
1584
+ }
1585
+ poller.start(POLLING_INTERVAL);
1502
1586
  }
1503
1587
  function useRoomNotificationSettings() {
1504
1588
  const room = useRoom();
@@ -1509,27 +1593,31 @@ function createRoomContext(client, options) {
1509
1593
  return () => decrementQuerySubscribers(queryKey);
1510
1594
  }, [room]);
1511
1595
  const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
1512
- return [
1513
- useSyncExternalStoreWithSelector(
1514
- store.subscribe,
1515
- store.get,
1516
- store.get,
1517
- (state) => {
1518
- const query = state.queries[makeNotificationSettingsQueryKey(room.id)];
1519
- if (query === void 0 || query.isLoading) {
1520
- return { isLoading: true };
1521
- }
1522
- if (query.error !== void 0) {
1523
- return { isLoading: false, error: query.error };
1524
- }
1525
- return {
1526
- isLoading: false,
1527
- settings: selectNotificationSettings(room.id, state)
1528
- };
1596
+ const selector = React2.useCallback(
1597
+ (state) => {
1598
+ const query = state.queries[makeNotificationSettingsQueryKey(room.id)];
1599
+ if (query === void 0 || query.isLoading) {
1600
+ return { isLoading: true };
1529
1601
  }
1530
- ),
1531
- updateRoomNotificationSettings
1532
- ];
1602
+ if (query.error !== void 0) {
1603
+ return { isLoading: false, error: query.error };
1604
+ }
1605
+ return {
1606
+ isLoading: false,
1607
+ settings: selectNotificationSettings(room.id, state)
1608
+ };
1609
+ },
1610
+ [room]
1611
+ );
1612
+ const settings = useSyncExternalStoreWithSelector(
1613
+ store.subscribe,
1614
+ store.get,
1615
+ store.get,
1616
+ selector
1617
+ );
1618
+ return React2.useMemo(() => {
1619
+ return [settings, updateRoomNotificationSettings];
1620
+ }, [settings, updateRoomNotificationSettings]);
1533
1621
  }
1534
1622
  function useRoomNotificationSettingsSuspense() {
1535
1623
  const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
@@ -1547,20 +1635,24 @@ function createRoomContext(client, options) {
1547
1635
  incrementQuerySubscribers(queryKey2);
1548
1636
  return () => decrementQuerySubscribers(queryKey2);
1549
1637
  }, [room]);
1550
- return [
1551
- useSyncExternalStoreWithSelector(
1552
- store.subscribe,
1553
- store.get,
1554
- store.get,
1555
- (state) => {
1556
- return {
1557
- isLoading: false,
1558
- settings: selectNotificationSettings(room.id, state)
1559
- };
1560
- }
1561
- ),
1562
- updateRoomNotificationSettings
1563
- ];
1638
+ const selector = React2.useCallback(
1639
+ (state) => {
1640
+ return {
1641
+ isLoading: false,
1642
+ settings: selectNotificationSettings(room.id, state)
1643
+ };
1644
+ },
1645
+ [room]
1646
+ );
1647
+ const settings = useSyncExternalStoreWithSelector(
1648
+ store.subscribe,
1649
+ store.get,
1650
+ store.get,
1651
+ selector
1652
+ );
1653
+ return React2.useMemo(() => {
1654
+ return [settings, updateRoomNotificationSettings];
1655
+ }, [settings, updateRoomNotificationSettings]);
1564
1656
  }
1565
1657
  function useUpdateRoomNotificationSettings() {
1566
1658
  const room = useRoom();
@@ -1573,7 +1665,7 @@ function createRoomContext(client, options) {
1573
1665
  roomId: room.id,
1574
1666
  settings
1575
1667
  });
1576
- room.updateRoomNotificationSettings(settings).then(
1668
+ room[kInternal].notifications.updateRoomNotificationSettings(settings).then(
1577
1669
  (settings2) => {
1578
1670
  store.set((state) => ({
1579
1671
  ...state,
@@ -1799,13 +1891,13 @@ function createSharedContext(client) {
1799
1891
  );
1800
1892
  const roomInfoState = getRoomInfoState();
1801
1893
  if (!roomInfoState || roomInfoState.isLoading) {
1802
- throw usersStore.get(roomId);
1894
+ throw roomsInfoStore.get(roomId);
1803
1895
  }
1804
1896
  if (roomInfoState.error) {
1805
1897
  throw roomInfoState.error;
1806
1898
  }
1807
1899
  const state = useSyncExternalStore2(
1808
- usersStore.subscribe,
1900
+ roomsInfoStore.subscribe,
1809
1901
  getRoomInfoState,
1810
1902
  getRoomInfoState
1811
1903
  );
@@ -1834,9 +1926,12 @@ function useLiveblocksContextBundle() {
1834
1926
  }
1835
1927
  return bundle;
1836
1928
  }
1929
+ var POLLING_INTERVAL2 = 60 * 1e3;
1930
+ var INBOX_NOTIFICATIONS_QUERY = "INBOX_NOTIFICATIONS";
1837
1931
  function createLiveblocksContext(client) {
1838
1932
  const shared = createSharedContext(client);
1839
1933
  const store = client[kInternal3].cacheStore;
1934
+ const notifications = client[kInternal3].notifications;
1840
1935
  function LiveblocksProvider(props) {
1841
1936
  return /* @__PURE__ */ React3.createElement(
1842
1937
  ContextBundle2.Provider,
@@ -1848,15 +1943,17 @@ function createLiveblocksContext(client) {
1848
1943
  }
1849
1944
  let fetchInboxNotificationsRequest = null;
1850
1945
  let inboxNotificationsSubscribers = 0;
1851
- const INBOX_NOTIFICATIONS_QUERY = "INBOX_NOTIFICATIONS";
1852
- const POLLING_INTERVAL2 = 60 * 1e3;
1946
+ let lastRequestedAt;
1853
1947
  const poller = makePoller2(refreshThreadsAndNotifications);
1854
1948
  function refreshThreadsAndNotifications() {
1855
- return client.getInboxNotifications().then(
1856
- ({ threads, inboxNotifications }) => {
1949
+ return notifications.getInboxNotifications({ since: lastRequestedAt }).then(
1950
+ (result) => {
1951
+ lastRequestedAt = result.meta.requestedAt;
1857
1952
  store.updateThreadsAndNotifications(
1858
- threads,
1859
- inboxNotifications,
1953
+ result.threads,
1954
+ result.inboxNotifications,
1955
+ result.deletedThreads,
1956
+ result.deletedInboxNotifications,
1860
1957
  INBOX_NOTIFICATIONS_QUERY
1861
1958
  );
1862
1959
  },
@@ -1888,13 +1985,18 @@ function createLiveblocksContext(client) {
1888
1985
  isLoading: true
1889
1986
  });
1890
1987
  try {
1891
- fetchInboxNotificationsRequest = client.getInboxNotifications();
1892
- const { inboxNotifications, threads } = await fetchInboxNotificationsRequest;
1988
+ fetchInboxNotificationsRequest = notifications.getInboxNotifications();
1989
+ const result = await fetchInboxNotificationsRequest;
1893
1990
  store.updateThreadsAndNotifications(
1894
- threads,
1895
- inboxNotifications,
1991
+ result.threads,
1992
+ result.inboxNotifications,
1993
+ result.deletedThreads,
1994
+ result.deletedInboxNotifications,
1896
1995
  INBOX_NOTIFICATIONS_QUERY
1897
1996
  );
1997
+ if (lastRequestedAt === void 0 || lastRequestedAt > result.meta.requestedAt) {
1998
+ lastRequestedAt = result.meta.requestedAt;
1999
+ }
1898
2000
  } catch (er) {
1899
2001
  store.setQueryState(INBOX_NOTIFICATIONS_QUERY, {
1900
2002
  isLoading: false,
@@ -1903,39 +2005,47 @@ function createLiveblocksContext(client) {
1903
2005
  }
1904
2006
  return;
1905
2007
  }
2008
+ function useInboxNotificationsSelectorCallback(state) {
2009
+ const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
2010
+ if (query === void 0 || query.isLoading) {
2011
+ return {
2012
+ isLoading: true
2013
+ };
2014
+ }
2015
+ if (query.error !== void 0) {
2016
+ return {
2017
+ error: query.error,
2018
+ isLoading: false
2019
+ };
2020
+ }
2021
+ return {
2022
+ inboxNotifications: selectedInboxNotifications(state),
2023
+ isLoading: false
2024
+ };
2025
+ }
1906
2026
  function useInboxNotifications() {
1907
2027
  useEffect5(() => {
1908
2028
  void fetchInboxNotifications();
1909
2029
  incrementInboxNotificationsSubscribers();
1910
2030
  return () => decrementInboxNotificationsSubscribers();
1911
- });
2031
+ }, []);
1912
2032
  const result = useSyncExternalStoreWithSelector2(
1913
2033
  store.subscribe,
1914
2034
  store.get,
1915
2035
  store.get,
1916
- (state) => {
1917
- const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
1918
- if (query === void 0 || query.isLoading) {
1919
- return {
1920
- isLoading: true
1921
- };
1922
- }
1923
- if (query.error !== void 0) {
1924
- return {
1925
- error: query.error,
1926
- isLoading: false
1927
- };
1928
- }
1929
- return {
1930
- inboxNotifications: selectedInboxNotifications(state),
1931
- isLoading: false
1932
- };
1933
- }
2036
+ useInboxNotificationsSelectorCallback
1934
2037
  );
1935
2038
  return result;
1936
2039
  }
2040
+ function useInboxNotificationsSuspenseSelector(state) {
2041
+ return {
2042
+ inboxNotifications: selectedInboxNotifications(state),
2043
+ isLoading: false
2044
+ };
2045
+ }
1937
2046
  function useInboxNotificationsSuspense() {
1938
- if (store.get().queries[INBOX_NOTIFICATIONS_QUERY] === void 0 || store.get().queries[INBOX_NOTIFICATIONS_QUERY].isLoading) {
2047
+ const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
2048
+ if (query === void 0 || query.isLoading) {
1939
2049
  throw fetchInboxNotifications();
1940
2050
  }
1941
2051
  React3.useEffect(() => {
@@ -1948,12 +2058,7 @@ function createLiveblocksContext(client) {
1948
2058
  store.subscribe,
1949
2059
  store.get,
1950
2060
  store.get,
1951
- (state) => {
1952
- return {
1953
- inboxNotifications: selectedInboxNotifications(state),
1954
- isLoading: false
1955
- };
1956
- }
2061
+ useInboxNotificationsSuspenseSelector
1957
2062
  );
1958
2063
  }
1959
2064
  function selectUnreadInboxNotificationsCount(state) {
@@ -1965,38 +2070,46 @@ function createLiveblocksContext(client) {
1965
2070
  }
1966
2071
  return count;
1967
2072
  }
2073
+ function useUnreadInboxNotificationsCountSelector(state) {
2074
+ const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
2075
+ if (query === void 0 || query.isLoading) {
2076
+ return {
2077
+ isLoading: true
2078
+ };
2079
+ }
2080
+ if (query.error !== void 0) {
2081
+ return {
2082
+ error: query.error,
2083
+ isLoading: false
2084
+ };
2085
+ }
2086
+ return {
2087
+ isLoading: false,
2088
+ count: selectUnreadInboxNotificationsCount(state)
2089
+ };
2090
+ }
1968
2091
  function useUnreadInboxNotificationsCount() {
1969
2092
  useEffect5(() => {
1970
2093
  void fetchInboxNotifications();
1971
2094
  incrementInboxNotificationsSubscribers();
1972
2095
  return () => decrementInboxNotificationsSubscribers();
1973
- });
2096
+ }, []);
1974
2097
  return useSyncExternalStoreWithSelector2(
1975
2098
  store.subscribe,
1976
2099
  store.get,
1977
2100
  store.get,
1978
- (state) => {
1979
- const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
1980
- if (query === void 0 || query.isLoading) {
1981
- return {
1982
- isLoading: true
1983
- };
1984
- }
1985
- if (query.error !== void 0) {
1986
- return {
1987
- error: query.error,
1988
- isLoading: false
1989
- };
1990
- }
1991
- return {
1992
- isLoading: false,
1993
- count: selectUnreadInboxNotificationsCount(state)
1994
- };
1995
- }
2101
+ useUnreadInboxNotificationsCountSelector
1996
2102
  );
1997
2103
  }
2104
+ function useUnreadInboxNotificationsCountSuspenseSelector(state) {
2105
+ return {
2106
+ isLoading: false,
2107
+ count: selectUnreadInboxNotificationsCount(state)
2108
+ };
2109
+ }
1998
2110
  function useUnreadInboxNotificationsCountSuspense() {
1999
- if (store.get().queries[INBOX_NOTIFICATIONS_QUERY] === void 0 || store.get().queries[INBOX_NOTIFICATIONS_QUERY].isLoading) {
2111
+ const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
2112
+ if (query === void 0 || query.isLoading) {
2000
2113
  throw fetchInboxNotifications();
2001
2114
  }
2002
2115
  React3.useEffect(() => {
@@ -2009,12 +2122,7 @@ function createLiveblocksContext(client) {
2009
2122
  store.subscribe,
2010
2123
  store.get,
2011
2124
  store.get,
2012
- (state) => {
2013
- return {
2014
- isLoading: false,
2015
- count: selectUnreadInboxNotificationsCount(state)
2016
- };
2017
- }
2125
+ useUnreadInboxNotificationsCountSuspenseSelector
2018
2126
  );
2019
2127
  }
2020
2128
  function useMarkInboxNotificationAsRead() {
@@ -2027,22 +2135,32 @@ function createLiveblocksContext(client) {
2027
2135
  inboxNotificationId,
2028
2136
  readAt
2029
2137
  });
2030
- client.markInboxNotificationAsRead(inboxNotificationId).then(
2138
+ notifications.markInboxNotificationAsRead(inboxNotificationId).then(
2031
2139
  () => {
2032
- store.set((state) => ({
2033
- ...state,
2034
- inboxNotifications: {
2035
- ...state.inboxNotifications,
2036
- [inboxNotificationId]: {
2037
- // TODO: Handle potential deleted inbox notification
2038
- ...state.inboxNotifications[inboxNotificationId],
2039
- readAt
2040
- }
2041
- },
2042
- optimisticUpdates: state.optimisticUpdates.filter(
2043
- (update) => update.id !== optimisticUpdateId
2044
- )
2045
- }));
2140
+ store.set((state) => {
2141
+ const existingNotification = state.inboxNotifications[inboxNotificationId];
2142
+ if (existingNotification === void 0) {
2143
+ return {
2144
+ ...state,
2145
+ optimisticUpdates: state.optimisticUpdates.filter(
2146
+ (update) => update.id !== optimisticUpdateId
2147
+ )
2148
+ };
2149
+ }
2150
+ return {
2151
+ ...state,
2152
+ inboxNotifications: {
2153
+ ...state.inboxNotifications,
2154
+ [inboxNotificationId]: {
2155
+ ...existingNotification,
2156
+ readAt
2157
+ }
2158
+ },
2159
+ optimisticUpdates: state.optimisticUpdates.filter(
2160
+ (update) => update.id !== optimisticUpdateId
2161
+ )
2162
+ };
2163
+ });
2046
2164
  },
2047
2165
  () => {
2048
2166
  store.set((state) => ({
@@ -2064,7 +2182,7 @@ function createLiveblocksContext(client) {
2064
2182
  id: optimisticUpdateId,
2065
2183
  readAt
2066
2184
  });
2067
- client.markAllInboxNotificationsAsRead().then(
2185
+ notifications.markAllInboxNotificationsAsRead().then(
2068
2186
  () => {
2069
2187
  store.set((state) => ({
2070
2188
  ...state,
@@ -2093,10 +2211,7 @@ function createLiveblocksContext(client) {
2093
2211
  }, []);
2094
2212
  }
2095
2213
  function useThreadFromCache(threadId) {
2096
- return useSyncExternalStoreWithSelector2(
2097
- store.subscribe,
2098
- store.get,
2099
- store.get,
2214
+ const selector = useCallback3(
2100
2215
  (state) => {
2101
2216
  const thread = state.threads[threadId];
2102
2217
  if (thread === void 0) {
@@ -2105,7 +2220,14 @@ function createLiveblocksContext(client) {
2105
2220
  );
2106
2221
  }
2107
2222
  return thread;
2108
- }
2223
+ },
2224
+ [threadId]
2225
+ );
2226
+ return useSyncExternalStoreWithSelector2(
2227
+ store.subscribe,
2228
+ store.get,
2229
+ store.get,
2230
+ selector
2109
2231
  );
2110
2232
  }
2111
2233
  const currentUserIdStore = client[kInternal3].currentUserIdStore;